mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-07 15:07:28 -06:00
Merge branch 'master' of https://github.com/Ultimaker/Cura into layerview_dev
This commit is contained in:
commit
bbd49cee85
80 changed files with 3683 additions and 1250 deletions
|
@ -1,6 +1,7 @@
|
|||
# Copyright (c) 2015 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
from UM.Scene.SceneNode import SceneNode
|
||||
from UM.Application import Application
|
||||
from UM.Resources import Resources
|
||||
|
@ -9,9 +10,11 @@ from UM.Math.Vector import Vector
|
|||
from UM.Math.Color import Color
|
||||
from UM.Math.AxisAlignedBox import AxisAlignedBox
|
||||
from UM.Math.Polygon import Polygon
|
||||
from UM.Message import Message
|
||||
|
||||
from UM.View.RenderBatch import RenderBatch
|
||||
from UM.View.GL.OpenGL import OpenGL
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
import numpy
|
||||
|
||||
|
@ -162,6 +165,13 @@ class BuildVolume(SceneNode):
|
|||
def getBoundingBox(self):
|
||||
return self._volume_aabb
|
||||
|
||||
def _buildVolumeMessage(self):
|
||||
Message(catalog.i18nc(
|
||||
"@info:status",
|
||||
"The build volume height has been reduced due to the value of the"
|
||||
" \"Print Sequence\" setting to prevent the gantry from colliding"
|
||||
" with printed objects."), lifetime=10).show()
|
||||
|
||||
def _onGlobalContainerStackChanged(self):
|
||||
if self._active_container_stack:
|
||||
self._active_container_stack.propertyChanged.disconnect(self._onSettingPropertyChanged)
|
||||
|
@ -174,6 +184,7 @@ class BuildVolume(SceneNode):
|
|||
self._width = self._active_container_stack.getProperty("machine_width", "value")
|
||||
if self._active_container_stack.getProperty("print_sequence", "value") == "one_at_a_time":
|
||||
self._height = self._active_container_stack.getProperty("gantry_height", "value")
|
||||
self._buildVolumeMessage()
|
||||
else:
|
||||
self._height = self._active_container_stack.getProperty("machine_height", "value")
|
||||
self._depth = self._active_container_stack.getProperty("machine_depth", "value")
|
||||
|
@ -189,6 +200,7 @@ class BuildVolume(SceneNode):
|
|||
if setting_key == "print_sequence":
|
||||
if Application.getInstance().getGlobalContainerStack().getProperty("print_sequence", "value") == "one_at_a_time":
|
||||
self._height = self._active_container_stack.getProperty("gantry_height", "value")
|
||||
self._buildVolumeMessage()
|
||||
else:
|
||||
self._height = self._active_container_stack.getProperty("machine_height", "value")
|
||||
self.rebuild()
|
||||
|
|
|
@ -19,7 +19,7 @@ from UM.JobQueue import JobQueue
|
|||
from UM.SaveFile import SaveFile
|
||||
from UM.Scene.Selection import Selection
|
||||
from UM.Scene.GroupDecorator import GroupDecorator
|
||||
import UM.Settings.Validator
|
||||
from UM.Settings.Validator import Validator
|
||||
|
||||
from UM.Operations.AddSceneNodeOperation import AddSceneNodeOperation
|
||||
from UM.Operations.RemoveSceneNodeOperation import RemoveSceneNodeOperation
|
||||
|
@ -32,8 +32,6 @@ from UM.Settings.ContainerRegistry import ContainerRegistry
|
|||
|
||||
from UM.i18n import i18nCatalog
|
||||
|
||||
from . import ExtruderManager
|
||||
from . import ExtrudersModel
|
||||
from . import PlatformPhysics
|
||||
from . import BuildVolume
|
||||
from . import CameraAnimation
|
||||
|
@ -42,13 +40,14 @@ from . import CuraActions
|
|||
from . import MultiMaterialDecorator
|
||||
from . import ZOffsetDecorator
|
||||
from . import CuraSplashScreen
|
||||
from . import MachineManagerModel
|
||||
from . import ContainerSettingsModel
|
||||
from . import CameraImageProvider
|
||||
from . import MachineActionManager
|
||||
|
||||
import cura.Settings
|
||||
|
||||
from PyQt5.QtCore import pyqtSlot, QUrl, pyqtSignal, pyqtProperty, QEvent, Q_ENUMS
|
||||
from PyQt5.QtGui import QColor, QIcon
|
||||
from PyQt5.QtWidgets import QMessageBox
|
||||
from PyQt5.QtQml import qmlRegisterUncreatableType, qmlRegisterSingletonType, qmlRegisterType
|
||||
|
||||
import platform
|
||||
|
@ -99,7 +98,32 @@ class CuraApplication(QtApplication):
|
|||
SettingDefinition.addSupportedProperty("settable_per_extruder", DefinitionPropertyType.Any, default = True)
|
||||
SettingDefinition.addSupportedProperty("settable_per_meshgroup", DefinitionPropertyType.Any, default = True)
|
||||
SettingDefinition.addSupportedProperty("settable_globally", DefinitionPropertyType.Any, default = True)
|
||||
SettingDefinition.addSettingType("extruder", int, str, UM.Settings.Validator)
|
||||
SettingDefinition.addSettingType("extruder", int, str, Validator)
|
||||
|
||||
## Add the 4 types of profiles to storage.
|
||||
Resources.addStorageType(self.ResourceTypes.QualityInstanceContainer, "quality")
|
||||
Resources.addStorageType(self.ResourceTypes.VariantInstanceContainer, "variants")
|
||||
Resources.addStorageType(self.ResourceTypes.MaterialInstanceContainer, "materials")
|
||||
Resources.addStorageType(self.ResourceTypes.UserInstanceContainer, "user")
|
||||
Resources.addStorageType(self.ResourceTypes.ExtruderStack, "extruders")
|
||||
Resources.addStorageType(self.ResourceTypes.MachineStack, "machine_instances")
|
||||
|
||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.QualityInstanceContainer)
|
||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.VariantInstanceContainer)
|
||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.MaterialInstanceContainer)
|
||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.UserInstanceContainer)
|
||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.ExtruderStack)
|
||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.MachineStack)
|
||||
|
||||
## Initialise the version upgrade manager with Cura's storage paths.
|
||||
import UM.VersionUpgradeManager #Needs to be here to prevent circular dependencies.
|
||||
self._version_upgrade_manager = UM.VersionUpgradeManager.VersionUpgradeManager(
|
||||
{
|
||||
("quality", UM.Settings.InstanceContainer.Version): (self.ResourceTypes.QualityInstanceContainer, "application/x-uranium-instancecontainer"),
|
||||
("machine_stack", UM.Settings.ContainerStack.Version): (self.ResourceTypes.MachineStack, "application/x-uranium-containerstack"),
|
||||
("preferences", UM.Preferences.Version): (Resources.Preferences, "application/x-uranium-preferences")
|
||||
}
|
||||
)
|
||||
|
||||
self._machine_action_manager = MachineActionManager.MachineActionManager()
|
||||
|
||||
|
@ -132,6 +156,9 @@ class CuraApplication(QtApplication):
|
|||
self._cura_actions = None
|
||||
self._started = False
|
||||
|
||||
self._message_box_callback = None
|
||||
self._message_box_callback_arguments = []
|
||||
|
||||
self._i18n_catalog = i18nCatalog("cura")
|
||||
|
||||
self.getController().getScene().sceneChanged.connect(self.updatePlatformActivity)
|
||||
|
@ -142,21 +169,6 @@ class CuraApplication(QtApplication):
|
|||
|
||||
self.showSplashMessage(self._i18n_catalog.i18nc("@info:progress", "Loading machines..."))
|
||||
|
||||
## Add the 4 types of profiles to storage.
|
||||
Resources.addStorageType(self.ResourceTypes.QualityInstanceContainer, "quality")
|
||||
Resources.addStorageType(self.ResourceTypes.VariantInstanceContainer, "variants")
|
||||
Resources.addStorageType(self.ResourceTypes.MaterialInstanceContainer, "materials")
|
||||
Resources.addStorageType(self.ResourceTypes.UserInstanceContainer, "user")
|
||||
Resources.addStorageType(self.ResourceTypes.ExtruderStack, "extruders")
|
||||
Resources.addStorageType(self.ResourceTypes.MachineStack, "machine_instances")
|
||||
|
||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.QualityInstanceContainer)
|
||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.VariantInstanceContainer)
|
||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.MaterialInstanceContainer)
|
||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.UserInstanceContainer)
|
||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.ExtruderStack)
|
||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.MachineStack)
|
||||
|
||||
# Add empty variant, material and quality containers.
|
||||
# Since they are empty, they should never be serialized and instead just programmatically created.
|
||||
# We need them to simplify the switching between materials.
|
||||
|
@ -223,6 +235,7 @@ class CuraApplication(QtApplication):
|
|||
meshfix
|
||||
blackmagic
|
||||
print_sequence
|
||||
infill_mesh
|
||||
dual
|
||||
experimental
|
||||
""".replace("\n", ";").replace(" ", ""))
|
||||
|
@ -242,6 +255,23 @@ class CuraApplication(QtApplication):
|
|||
def _onEngineCreated(self):
|
||||
self._engine.addImageProvider("camera", CameraImageProvider.CameraImageProvider())
|
||||
|
||||
## A reusable dialogbox
|
||||
#
|
||||
showMessageBox = pyqtSignal(str, str, str, str, int, int, arguments = ["title", "text", "informativeText", "detailedText", "buttons", "icon"])
|
||||
def messageBox(self, title, text, informativeText = "", detailedText = "", buttons = QMessageBox.Ok, icon = QMessageBox.NoIcon, callback = None, callback_arguments = []):
|
||||
self._message_box_callback = callback
|
||||
self._message_box_callback_arguments = callback_arguments
|
||||
self.showMessageBox.emit(title, text, informativeText, detailedText, buttons, icon)
|
||||
|
||||
@pyqtSlot(int)
|
||||
def messageBoxClosed(self, button):
|
||||
if self._message_box_callback:
|
||||
self._message_box_callback(button, *self._message_box_callback_arguments)
|
||||
self._message_box_callback = None
|
||||
self._message_box_callback_arguments = []
|
||||
|
||||
showPrintMonitor = pyqtSignal(bool, arguments = ["show"])
|
||||
|
||||
## Cura has multiple locations where instance containers need to be saved, so we need to handle this differently.
|
||||
#
|
||||
# Note that the AutoSave plugin also calls this method.
|
||||
|
@ -261,7 +291,8 @@ class CuraApplication(QtApplication):
|
|||
Logger.logException("e", "An exception occurred when serializing container %s", instance.getId())
|
||||
continue
|
||||
|
||||
file_name = urllib.parse.quote_plus(instance.getId()) + ".inst.cfg"
|
||||
mime_type = ContainerRegistry.getMimeTypeForContainer(type(instance))
|
||||
file_name = urllib.parse.quote_plus(instance.getId()) + "." + mime_type.preferredSuffix
|
||||
instance_type = instance.getMetaDataEntry("type")
|
||||
path = None
|
||||
if instance_type == "material":
|
||||
|
@ -289,7 +320,8 @@ class CuraApplication(QtApplication):
|
|||
Logger.logException("e", "An exception occurred when serializing container %s", instance.getId())
|
||||
continue
|
||||
|
||||
file_name = urllib.parse.quote_plus(stack.getId()) + ".stack.cfg"
|
||||
mime_type = ContainerRegistry.getMimeTypeForContainer(type(stack))
|
||||
file_name = urllib.parse.quote_plus(stack.getId()) + "." + mime_type.preferredSuffix
|
||||
stack_type = stack.getMetaDataEntry("type", None)
|
||||
path = None
|
||||
if not stack_type or stack_type == "machine":
|
||||
|
@ -366,9 +398,9 @@ class CuraApplication(QtApplication):
|
|||
self.showSplashMessage(self._i18n_catalog.i18nc("@info:progress", "Loading interface..."))
|
||||
|
||||
# Initialise extruder so as to listen to global container stack changes before the first global container stack is set.
|
||||
ExtruderManager.ExtruderManager.getInstance()
|
||||
qmlRegisterSingletonType(MachineManagerModel.MachineManagerModel, "Cura", 1, 0, "MachineManager",
|
||||
MachineManagerModel.createMachineManagerModel)
|
||||
cura.Settings.ExtruderManager.getInstance()
|
||||
qmlRegisterSingletonType(cura.Settings.MachineManager, "Cura", 1, 0, "MachineManager",
|
||||
cura.Settings.MachineManager.createMachineManager)
|
||||
|
||||
qmlRegisterSingletonType(MachineActionManager.MachineActionManager, "Cura", 1, 0, "MachineActionManager", self.getMachineActionManager)
|
||||
self.setMainQml(Resources.getPath(self.ResourceTypes.QmlFiles, "Cura.qml"))
|
||||
|
@ -412,6 +444,7 @@ class CuraApplication(QtApplication):
|
|||
# \param engine The QML engine.
|
||||
def registerObjects(self, engine):
|
||||
engine.rootContext().setContextProperty("Printer", self)
|
||||
engine.rootContext().setContextProperty("CuraApplication", self)
|
||||
self._print_information = PrintInformation.PrintInformation()
|
||||
engine.rootContext().setContextProperty("PrintInformation", self._print_information)
|
||||
self._cura_actions = CuraActions.CuraActions(self)
|
||||
|
@ -419,13 +452,16 @@ class CuraApplication(QtApplication):
|
|||
|
||||
qmlRegisterUncreatableType(CuraApplication, "Cura", 1, 0, "ResourceTypes", "Just an Enum type")
|
||||
|
||||
qmlRegisterType(ExtrudersModel.ExtrudersModel, "Cura", 1, 0, "ExtrudersModel")
|
||||
qmlRegisterType(cura.Settings.ExtrudersModel, "Cura", 1, 0, "ExtrudersModel")
|
||||
|
||||
qmlRegisterType(ContainerSettingsModel.ContainerSettingsModel, "Cura", 1, 0, "ContainerSettingsModel")
|
||||
qmlRegisterType(cura.Settings.ContainerSettingsModel, "Cura", 1, 0, "ContainerSettingsModel")
|
||||
qmlRegisterType(cura.Settings.MaterialSettingsVisibilityHandler, "Cura", 1, 0, "MaterialSettingsVisibilityHandler")
|
||||
|
||||
qmlRegisterSingletonType(cura.Settings.ContainerManager, "Cura", 1, 0, "ContainerManager", cura.Settings.ContainerManager.createContainerManager)
|
||||
|
||||
qmlRegisterSingletonType(QUrl.fromLocalFile(Resources.getPath(CuraApplication.ResourceTypes.QmlFiles, "Actions.qml")), "Cura", 1, 0, "Actions")
|
||||
|
||||
engine.rootContext().setContextProperty("ExtruderManager", ExtruderManager.ExtruderManager.getInstance())
|
||||
engine.rootContext().setContextProperty("ExtruderManager", cura.Settings.ExtruderManager.getInstance())
|
||||
|
||||
for path in Resources.getAllResourcesOfType(CuraApplication.ResourceTypes.QmlFiles):
|
||||
type_name = os.path.splitext(os.path.basename(path))[0]
|
||||
|
@ -546,12 +582,12 @@ class CuraApplication(QtApplication):
|
|||
for _ in range(count):
|
||||
if node.getParent() and node.getParent().callDecoration("isGroup"):
|
||||
new_node = copy.deepcopy(node.getParent()) #Copy the group node.
|
||||
new_node.callDecoration("setConvexHull",None)
|
||||
new_node.callDecoration("recomputeConvexHull")
|
||||
|
||||
op.addOperation(AddSceneNodeOperation(new_node,node.getParent().getParent()))
|
||||
else:
|
||||
new_node = copy.deepcopy(node)
|
||||
new_node.callDecoration("setConvexHull", None)
|
||||
new_node.callDecoration("recomputeConvexHull")
|
||||
op.addOperation(AddSceneNodeOperation(new_node, node.getParent()))
|
||||
|
||||
op.push()
|
||||
|
@ -576,6 +612,7 @@ class CuraApplication(QtApplication):
|
|||
## Delete all nodes containing mesh data in the scene.
|
||||
@pyqtSlot()
|
||||
def deleteAll(self):
|
||||
Logger.log("i", "Clearing scene")
|
||||
if not self.getController().getToolsEnabled():
|
||||
return
|
||||
|
||||
|
@ -599,6 +636,7 @@ class CuraApplication(QtApplication):
|
|||
## Reset all translation on nodes with mesh data.
|
||||
@pyqtSlot()
|
||||
def resetAllTranslation(self):
|
||||
Logger.log("i", "Resetting all scene translations")
|
||||
nodes = []
|
||||
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
|
||||
if type(node) is not SceneNode:
|
||||
|
@ -621,6 +659,7 @@ class CuraApplication(QtApplication):
|
|||
## Reset all transformations on nodes with mesh data.
|
||||
@pyqtSlot()
|
||||
def resetAll(self):
|
||||
Logger.log("i", "Resetting all scene transformations")
|
||||
nodes = []
|
||||
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
|
||||
if type(node) is not SceneNode:
|
||||
|
@ -644,6 +683,7 @@ class CuraApplication(QtApplication):
|
|||
## Reload all mesh data on the screen from file.
|
||||
@pyqtSlot()
|
||||
def reloadAll(self):
|
||||
Logger.log("i", "Reloading all loaded mesh data.")
|
||||
nodes = []
|
||||
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
|
||||
if type(node) is not SceneNode or not node.getMeshData():
|
||||
|
@ -655,15 +695,14 @@ class CuraApplication(QtApplication):
|
|||
return
|
||||
|
||||
for node in nodes:
|
||||
if not node.getMeshData():
|
||||
continue
|
||||
|
||||
file_name = node.getMeshData().getFileName()
|
||||
if file_name:
|
||||
job = ReadMeshJob(file_name)
|
||||
job._node = node
|
||||
job.finished.connect(self._reloadMeshFinished)
|
||||
job.start()
|
||||
else:
|
||||
Logger.log("w", "Unable to reload data because we don't have a filename.")
|
||||
|
||||
## Get logging data of the backend engine
|
||||
# \returns \type{string} Logging data
|
||||
|
|
|
@ -25,10 +25,13 @@ class CuraSplashScreen(QSplashScreen):
|
|||
if buildtype:
|
||||
version[0] += " (%s)" %(buildtype)
|
||||
|
||||
painter.setFont(QFont("Proxima Nova Rg", 20 ))
|
||||
font = QFont() # Using system-default font here
|
||||
font.setPointSize(20)
|
||||
painter.setFont(font)
|
||||
painter.drawText(0, 0, 330 * self._scale, 230 * self._scale, Qt.AlignHCenter | Qt.AlignBottom, version[0])
|
||||
if len(version) > 1:
|
||||
painter.setFont(QFont("Proxima Nova Rg", 12 ))
|
||||
font.setPointSize(12)
|
||||
painter.setFont(font)
|
||||
painter.drawText(0, 0, 330 * self._scale, 255 * self._scale, Qt.AlignHCenter | Qt.AlignBottom, version[1])
|
||||
|
||||
painter.restore()
|
||||
|
|
|
@ -6,3 +6,6 @@ class MultiMaterialDecorator(SceneNodeDecorator):
|
|||
|
||||
def isMultiMaterial(self):
|
||||
return True
|
||||
|
||||
def __deepcopy__(self, memo):
|
||||
return MultiMaterialDecorator()
|
|
@ -7,7 +7,6 @@ from UM.Scene.SceneNode import SceneNode
|
|||
from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator
|
||||
from UM.Math.Vector import Vector
|
||||
from UM.Math.AxisAlignedBox import AxisAlignedBox
|
||||
from UM.Application import Application
|
||||
from UM.Scene.Selection import Selection
|
||||
from UM.Preferences import Preferences
|
||||
|
||||
|
@ -16,8 +15,6 @@ from cura.ConvexHullDecorator import ConvexHullDecorator
|
|||
from . import PlatformPhysicsOperation
|
||||
from . import ZOffsetDecorator
|
||||
|
||||
import copy
|
||||
|
||||
class PlatformPhysics:
|
||||
def __init__(self, controller, volume):
|
||||
super().__init__()
|
||||
|
@ -66,9 +63,6 @@ class PlatformPhysics:
|
|||
elif bbox.bottom < z_offset:
|
||||
move_vector = move_vector.set(y=(-bbox.bottom) - z_offset)
|
||||
|
||||
#if not Float.fuzzyCompare(bbox.bottom, 0.0):
|
||||
# pass#move_vector.setY(-bbox.bottom)
|
||||
|
||||
# If there is no convex hull for the node, start calculating it and continue.
|
||||
if not node.getDecorator(ConvexHullDecorator):
|
||||
node.addDecorator(ConvexHullDecorator())
|
||||
|
@ -81,26 +75,19 @@ class PlatformPhysics:
|
|||
if other_node is root or type(other_node) is not SceneNode or other_node is node:
|
||||
continue
|
||||
|
||||
# Ignore colissions of a group with it's own children
|
||||
# Ignore collisions of a group with it's own children
|
||||
if other_node in node.getAllChildren() or node in other_node.getAllChildren():
|
||||
continue
|
||||
|
||||
# Ignore colissions within a group
|
||||
# Ignore collisions within a group
|
||||
if other_node.getParent().callDecoration("isGroup") is not None or node.getParent().callDecoration("isGroup") is not None:
|
||||
continue
|
||||
#if node.getParent().callDecoration("isGroup") is other_node.getParent().callDecoration("isGroup"):
|
||||
# continue
|
||||
|
||||
# Ignore nodes that do not have the right properties set.
|
||||
if not other_node.callDecoration("getConvexHull") or not other_node.getBoundingBox():
|
||||
continue
|
||||
|
||||
# Check to see if the bounding boxes intersect. If not, we can ignore the node as there is no way the hull intersects.
|
||||
#if node.getBoundingBox().intersectsBox(other_node.getBoundingBox()) == AxisAlignedBox.IntersectionResult.NoIntersection:
|
||||
# continue
|
||||
|
||||
# Get the overlap distance for both convex hulls. If this returns None, there is no intersection.
|
||||
try:
|
||||
head_hull = node.callDecoration("getConvexHullHead")
|
||||
if head_hull:
|
||||
overlap = head_hull.intersectsPolygon(other_node.callDecoration("getConvexHull"))
|
||||
|
@ -109,9 +96,14 @@ class PlatformPhysics:
|
|||
if other_head_hull:
|
||||
overlap = node.callDecoration("getConvexHull").intersectsPolygon(other_head_hull)
|
||||
else:
|
||||
overlap = node.callDecoration("getConvexHull").intersectsPolygon(other_node.callDecoration("getConvexHull"))
|
||||
except:
|
||||
overlap = None #It can sometimes occur that the calculated convex hull has no size, in which case there is no overlap.
|
||||
own_convex_hull = node.callDecoration("getConvexHull")
|
||||
other_convex_hull = other_node.callDecoration("getConvexHull")
|
||||
if own_convex_hull and other_convex_hull:
|
||||
overlap = own_convex_hull.intersectsPolygon(other_convex_hull)
|
||||
else:
|
||||
# This can happen in some cases if the object is not yet done with being loaded.
|
||||
# Simply waiting for the next tick seems to resolve this correctly.
|
||||
overlap = None
|
||||
|
||||
if overlap is None:
|
||||
continue
|
||||
|
|
|
@ -44,7 +44,7 @@ class PrintInformation(QObject):
|
|||
|
||||
self._current_print_time = Duration(None, self)
|
||||
|
||||
self._material_amount = -1
|
||||
self._material_amounts = []
|
||||
|
||||
self._backend = Application.getInstance().getBackend()
|
||||
if self._backend:
|
||||
|
@ -62,21 +62,22 @@ class PrintInformation(QObject):
|
|||
def currentPrintTime(self):
|
||||
return self._current_print_time
|
||||
|
||||
materialAmountChanged = pyqtSignal()
|
||||
materialAmountsChanged = pyqtSignal()
|
||||
|
||||
@pyqtProperty(float, notify = materialAmountChanged)
|
||||
def materialAmount(self):
|
||||
return self._material_amount
|
||||
@pyqtProperty("QVariantList", notify = materialAmountsChanged)
|
||||
def materialAmounts(self):
|
||||
return self._material_amounts
|
||||
|
||||
def _onPrintDurationMessage(self, time, amount):
|
||||
#if self._slice_pass == self.SlicePass.CurrentSettings:
|
||||
self._current_print_time.setDuration(time)
|
||||
def _onPrintDurationMessage(self, total_time, material_amounts):
|
||||
self._current_print_time.setDuration(total_time)
|
||||
self.currentPrintTimeChanged.emit()
|
||||
|
||||
# Material amount is sent as an amount of mm^3, so calculate length from that
|
||||
r = Application.getInstance().getGlobalContainerStack().getProperty("material_diameter", "value") / 2
|
||||
self._material_amount = round((amount / (math.pi * r ** 2)) / 1000, 2)
|
||||
self.materialAmountChanged.emit()
|
||||
self._material_amounts = []
|
||||
for amount in material_amounts:
|
||||
self._material_amounts.append(round((amount / (math.pi * r ** 2)) / 1000, 2))
|
||||
self.materialAmountsChanged.emit()
|
||||
|
||||
@pyqtSlot(str)
|
||||
def setJobName(self, name):
|
||||
|
|
|
@ -24,6 +24,8 @@ class PrinterOutputDevice(QObject, OutputDevice):
|
|||
self._num_extruders = 1
|
||||
self._hotend_temperatures = [0] * self._num_extruders
|
||||
self._target_hotend_temperatures = [0] * self._num_extruders
|
||||
self._material_ids = [""] * self._num_extruders
|
||||
self._hotend_ids = [""] * self._num_extruders
|
||||
self._progress = 0
|
||||
self._head_x = 0
|
||||
self._head_y = 0
|
||||
|
@ -57,6 +59,12 @@ class PrinterOutputDevice(QObject, OutputDevice):
|
|||
# Signal to be emitted when head position is changed (x,y,z)
|
||||
headPositionChanged = pyqtSignal()
|
||||
|
||||
# Signal to be emitted when either of the material ids is changed
|
||||
materialIdChanged = pyqtSignal(int, str, arguments = ["index", "id"])
|
||||
|
||||
# Signal to be emitted when either of the hotend ids is changed
|
||||
hotendIdChanged = pyqtSignal(int, str, arguments = ["index", "id"])
|
||||
|
||||
# Signal that is emitted every time connection state is changed.
|
||||
# it also sends it's own device_id (for convenience sake)
|
||||
connectionStateChanged = pyqtSignal(str)
|
||||
|
@ -212,6 +220,34 @@ class PrinterOutputDevice(QObject, OutputDevice):
|
|||
self._hotend_temperatures[index] = temperature
|
||||
self.hotendTemperaturesChanged.emit()
|
||||
|
||||
@pyqtProperty("QVariantList", notify = materialIdChanged)
|
||||
def materialIds(self):
|
||||
return self._material_ids
|
||||
|
||||
## Protected setter for the current material id.
|
||||
# /param index Index of the extruder
|
||||
# /param material_id id of the material
|
||||
def _setMaterialId(self, index, material_id):
|
||||
if material_id and material_id != "" and material_id != self._material_ids[index]:
|
||||
Logger.log("d", "Setting material id of hotend %d to %s" % (index, material_id))
|
||||
self._material_ids[index] = material_id
|
||||
self.materialIdChanged.emit(index, material_id)
|
||||
|
||||
|
||||
@pyqtProperty("QVariantList", notify = hotendIdChanged)
|
||||
def hotendIds(self):
|
||||
return self._hotend_ids
|
||||
|
||||
## Protected setter for the current hotend id.
|
||||
# /param index Index of the extruder
|
||||
# /param hotend_id id of the hotend
|
||||
def _setHotendId(self, index, hotend_id):
|
||||
if hotend_id and hotend_id != "" and hotend_id != self._hotend_ids[index]:
|
||||
Logger.log("d", "Setting hotend id of hotend %d to %s" % (index, hotend_id))
|
||||
self._hotend_ids[index] = hotend_id
|
||||
self.hotendIdChanged.emit(index, hotend_id)
|
||||
|
||||
|
||||
## Attempt to establish connection
|
||||
def connect(self):
|
||||
raise NotImplementedError("connect needs to be implemented")
|
||||
|
|
387
cura/Settings/ContainerManager.py
Normal file
387
cura/Settings/ContainerManager.py
Normal file
|
@ -0,0 +1,387 @@
|
|||
# Copyright (c) 2016 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
import os.path
|
||||
import urllib
|
||||
|
||||
from PyQt5.QtCore import QObject, pyqtSlot, pyqtProperty, pyqtSignal, QUrl
|
||||
from PyQt5.QtWidgets import QMessageBox
|
||||
|
||||
import UM.PluginRegistry
|
||||
import UM.Settings
|
||||
import UM.SaveFile
|
||||
import UM.Platform
|
||||
import UM.MimeTypeDatabase
|
||||
import UM.Logger
|
||||
|
||||
from UM.MimeTypeDatabase import MimeTypeNotFoundError
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
## Manager class that contains common actions to deal with containers in Cura.
|
||||
#
|
||||
# This is primarily intended as a class to be able to perform certain actions
|
||||
# from within QML. We want to be able to trigger things like removing a container
|
||||
# when a certain action happens. This can be done through this class.
|
||||
class ContainerManager(QObject):
|
||||
def __init__(self, parent = None):
|
||||
super().__init__(parent)
|
||||
|
||||
self._registry = UM.Settings.ContainerRegistry.getInstance()
|
||||
self._container_name_filters = {}
|
||||
|
||||
## Create a duplicate of the specified container
|
||||
#
|
||||
# This will create and add a duplicate of the container corresponding
|
||||
# to the container ID.
|
||||
#
|
||||
# \param container_id \type{str} The ID of the container to duplicate.
|
||||
#
|
||||
# \return The ID of the new container, or an empty string if duplication failed.
|
||||
@pyqtSlot(str, result = str)
|
||||
def duplicateContainer(self, container_id):
|
||||
containers = self._registry.findContainers(None, id = container_id)
|
||||
if not containers:
|
||||
UM.Logger.log("w", "Could duplicate container %s because it was not found.", container_id)
|
||||
return ""
|
||||
|
||||
container = containers[0]
|
||||
|
||||
new_container = None
|
||||
new_name = self._registry.uniqueName(container.getName())
|
||||
# Only InstanceContainer has a duplicate method at the moment.
|
||||
# So fall back to serialize/deserialize when no duplicate method exists.
|
||||
if hasattr(container, "duplicate"):
|
||||
new_container = container.duplicate(new_name)
|
||||
else:
|
||||
new_container = container.__class__(new_name)
|
||||
new_container.deserialize(container.serialize())
|
||||
new_container.setName(new_name)
|
||||
|
||||
if new_container:
|
||||
self._registry.addContainer(new_container)
|
||||
|
||||
return new_container.getId()
|
||||
|
||||
## Change the name of a specified container to a new name.
|
||||
#
|
||||
# \param container_id \type{str} The ID of the container to change the name of.
|
||||
# \param new_id \type{str} The new ID of the container.
|
||||
# \param new_name \type{str} The new name of the specified container.
|
||||
#
|
||||
# \return True if successful, False if not.
|
||||
@pyqtSlot(str, str, str, result = bool)
|
||||
def renameContainer(self, container_id, new_id, new_name):
|
||||
containers = self._registry.findContainers(None, id = container_id)
|
||||
if not containers:
|
||||
UM.Logger.log("w", "Could rename container %s because it was not found.", container_id)
|
||||
return False
|
||||
|
||||
container = containers[0]
|
||||
# First, remove the container from the registry. This will clean up any files related to the container.
|
||||
self._registry.removeContainer(container)
|
||||
|
||||
# Ensure we have a unique name for the container
|
||||
new_name = self._registry.uniqueName(new_name)
|
||||
|
||||
# Then, update the name and ID of the container
|
||||
container.setName(new_name)
|
||||
container._id = new_id # TODO: Find a nicer way to set a new, unique ID
|
||||
|
||||
# Finally, re-add the container so it will be properly serialized again.
|
||||
self._registry.addContainer(container)
|
||||
|
||||
return True
|
||||
|
||||
## Remove the specified container.
|
||||
#
|
||||
# \param container_id \type{str} The ID of the container to remove.
|
||||
#
|
||||
# \return True if the container was successfully removed, False if not.
|
||||
@pyqtSlot(str, result = bool)
|
||||
def removeContainer(self, container_id):
|
||||
containers = self._registry.findContainers(None, id = container_id)
|
||||
if not containers:
|
||||
UM.Logger.log("w", "Could remove container %s because it was not found.", container_id)
|
||||
return False
|
||||
|
||||
self._registry.removeContainer(containers[0].getId())
|
||||
|
||||
return True
|
||||
|
||||
## Merge a container with another.
|
||||
#
|
||||
# This will try to merge one container into the other, by going through the container
|
||||
# and setting the right properties on the other container.
|
||||
#
|
||||
# \param merge_into_id \type{str} The ID of the container to merge into.
|
||||
# \param merge_id \type{str} The ID of the container to merge.
|
||||
#
|
||||
# \return True if successfully merged, False if not.
|
||||
@pyqtSlot(str, result = bool)
|
||||
def mergeContainers(self, merge_into_id, merge_id):
|
||||
containers = self._registry.findContainers(None, id = merge_into_id)
|
||||
if not containers:
|
||||
UM.Logger.log("w", "Could merge into container %s because it was not found.", merge_into_id)
|
||||
return False
|
||||
|
||||
merge_into = containers[0]
|
||||
|
||||
containers = self._registry.findContainers(None, id = merge_id)
|
||||
if not containers:
|
||||
UM.Logger.log("w", "Could not merge container %s because it was not found", merge_id)
|
||||
return False
|
||||
|
||||
merge = containers[0]
|
||||
|
||||
if type(merge) != type(merge_into):
|
||||
UM.Logger.log("w", "Cannot merge two containers of different types")
|
||||
return False
|
||||
|
||||
for key in merge.getAllKeys():
|
||||
merge_into.setProperty(key, "value", merge.getProperty(key, "value"))
|
||||
|
||||
return True
|
||||
|
||||
## Clear the contents of a container.
|
||||
#
|
||||
# \param container_id \type{str} The ID of the container to clear.
|
||||
#
|
||||
# \return True if successful, False if not.
|
||||
@pyqtSlot(str, result = bool)
|
||||
def clearContainer(self, container_id):
|
||||
containers = self._registry.findContainers(None, id = container_id)
|
||||
if not containers:
|
||||
UM.Logger.log("w", "Could clear container %s because it was not found.", container_id)
|
||||
return False
|
||||
|
||||
if containers[0].isReadOnly():
|
||||
UM.Logger.log("w", "Cannot clear read-only container %s", container_id)
|
||||
return False
|
||||
|
||||
containers[0].clear()
|
||||
|
||||
return True
|
||||
|
||||
## Set a metadata entry of the specified container.
|
||||
#
|
||||
# This will set the specified entry of the container's metadata to the specified
|
||||
# value. Note that entries containing dictionaries can have their entries changed
|
||||
# by using "/" as a separator. For example, to change an entry "foo" in a
|
||||
# dictionary entry "bar", you can specify "bar/foo" as entry name.
|
||||
#
|
||||
# \param container_id \type{str} The ID of the container to change.
|
||||
# \param entry_name \type{str} The name of the metadata entry to change.
|
||||
# \param entry_value The new value of the entry.
|
||||
#
|
||||
# \return True if successful, False if not.
|
||||
@pyqtSlot(str, str, str, result = bool)
|
||||
def setContainerMetaDataEntry(self, container_id, entry_name, entry_value):
|
||||
containers = UM.Settings.ContainerRegistry.getInstance().findContainers(None, id = container_id)
|
||||
if not containers:
|
||||
UM.Logger.log("w", "Could set metadata of container %s because it was not found.", container_id)
|
||||
return False
|
||||
|
||||
container = containers[0]
|
||||
|
||||
if container.isReadOnly():
|
||||
UM.Logger.log("w", "Cannot set metadata of read-only container %s.", container_id)
|
||||
return False
|
||||
|
||||
entries = entry_name.split("/")
|
||||
entry_name = entries.pop()
|
||||
|
||||
if entries:
|
||||
root_name = entries.pop(0)
|
||||
root = container.getMetaDataEntry(root_name)
|
||||
|
||||
item = root
|
||||
for entry in entries:
|
||||
item = item.get(entries.pop(0), { })
|
||||
|
||||
item[entry_name] = entry_value
|
||||
|
||||
entry_name = root_name
|
||||
entry_value = root
|
||||
|
||||
container.setMetaDataEntry(entry_name, entry_value)
|
||||
|
||||
return True
|
||||
|
||||
## Find instance containers matching certain criteria.
|
||||
#
|
||||
# This effectively forwards to ContainerRegistry::findInstanceContainers.
|
||||
#
|
||||
# \param criteria A dict of key - value pairs to search for.
|
||||
#
|
||||
# \return A list of container IDs that match the given criteria.
|
||||
@pyqtSlot("QVariantMap", result = "QVariantList")
|
||||
def findInstanceContainers(self, criteria):
|
||||
result = []
|
||||
for entry in self._registry.findInstanceContainers(**criteria):
|
||||
result.append(entry.getId())
|
||||
|
||||
return result
|
||||
|
||||
## Get a list of string that can be used as name filters for a Qt File Dialog
|
||||
#
|
||||
# This will go through the list of available container types and generate a list of strings
|
||||
# out of that. The strings are formatted as "description (*.extension)" and can be directly
|
||||
# passed to a nameFilters property of a Qt File Dialog.
|
||||
#
|
||||
# \param type_name Which types of containers to list. These types correspond to the "type"
|
||||
# key of the plugin metadata.
|
||||
#
|
||||
# \return A string list with name filters.
|
||||
@pyqtSlot(str, result = "QStringList")
|
||||
def getContainerNameFilters(self, type_name):
|
||||
if not self._container_name_filters:
|
||||
self._updateContainerNameFilters()
|
||||
|
||||
filters = []
|
||||
for filter_string, entry in self._container_name_filters.items():
|
||||
if not type_name or entry["type"] == type_name:
|
||||
filters.append(filter_string)
|
||||
|
||||
return filters
|
||||
|
||||
## Export a container to a file
|
||||
#
|
||||
# \param container_id The ID of the container to export
|
||||
# \param file_type The type of file to save as. Should be in the form of "description (*.extension, *.ext)"
|
||||
# \param file_url The URL where to save the file.
|
||||
#
|
||||
# \return A dictionary containing a key "status" with a status code and a key "message" with a message
|
||||
# explaining the status.
|
||||
# The status code can be one of "error", "cancelled", "success"
|
||||
@pyqtSlot(str, str, QUrl, result = "QVariantMap")
|
||||
def exportContainer(self, container_id, file_type, file_url):
|
||||
if not container_id or not file_type or not file_url:
|
||||
return { "status": "error", "message": "Invalid arguments"}
|
||||
|
||||
if isinstance(file_url, QUrl):
|
||||
file_url = file_url.toLocalFile()
|
||||
|
||||
if not file_url:
|
||||
return { "status": "error", "message": "Invalid path"}
|
||||
|
||||
mime_type = None
|
||||
if not file_type in self._container_name_filters:
|
||||
try:
|
||||
mime_type = UM.MimeTypeDatabase.getMimeTypeForFile(file_url)
|
||||
except MimeTypeNotFoundError:
|
||||
return { "status": "error", "message": "Unknown File Type" }
|
||||
else:
|
||||
mime_type = self._container_name_filters[file_type]["mime"]
|
||||
|
||||
containers = UM.Settings.ContainerRegistry.getInstance().findContainers(None, id = container_id)
|
||||
if not containers:
|
||||
return { "status": "error", "message": "Container not found"}
|
||||
container = containers[0]
|
||||
|
||||
for suffix in mime_type.suffixes:
|
||||
if file_url.endswith(suffix):
|
||||
break
|
||||
else:
|
||||
file_url += "." + mime_type.preferredSuffix
|
||||
|
||||
if not UM.Platform.isWindows():
|
||||
if os.path.exists(file_url):
|
||||
result = QMessageBox.question(None, catalog.i18nc("@title:window", "File Already Exists"),
|
||||
catalog.i18nc("@label", "The file <filename>{0}</filename> already exists. Are you sure you want to overwrite it?").format(file_url))
|
||||
if result == QMessageBox.No:
|
||||
return { "status": "cancelled", "message": "User cancelled"}
|
||||
|
||||
try:
|
||||
contents = container.serialize()
|
||||
except NotImplementedError:
|
||||
return { "status": "error", "message": "Unable to serialize container"}
|
||||
|
||||
with UM.SaveFile(file_url, "w") as f:
|
||||
f.write(contents)
|
||||
|
||||
return { "status": "success", "message": "Succesfully exported container"}
|
||||
|
||||
## Imports a profile from a file
|
||||
#
|
||||
# \param file_url A URL that points to the file to import.
|
||||
#
|
||||
# \return \type{Dict} dict with a 'status' key containing the string 'success' or 'error', and a 'message' key
|
||||
# containing a message for the user
|
||||
@pyqtSlot(QUrl, result = "QVariantMap")
|
||||
def importContainer(self, file_url):
|
||||
if not file_url:
|
||||
return { "status": "error", "message": "Invalid path"}
|
||||
|
||||
if isinstance(file_url, QUrl):
|
||||
file_url = file_url.toLocalFile()
|
||||
|
||||
if not file_url or not os.path.exists(file_url):
|
||||
return { "status": "error", "message": "Invalid path" }
|
||||
|
||||
try:
|
||||
mime_type = UM.MimeTypeDatabase.getMimeTypeForFile(file_url)
|
||||
except MimeTypeNotFoundError:
|
||||
return { "status": "error", "message": "Could not determine mime type of file" }
|
||||
|
||||
container_type = UM.Settings.ContainerRegistry.getContainerForMimeType(mime_type)
|
||||
if not container_type:
|
||||
return { "status": "error", "message": "Could not find a container to handle the specified file."}
|
||||
|
||||
container_id = urllib.parse.unquote_plus(mime_type.stripExtension(os.path.basename(file_url)))
|
||||
container_id = UM.Settings.ContainerRegistry.getInstance().uniqueName(container_id)
|
||||
|
||||
container = container_type(container_id)
|
||||
|
||||
try:
|
||||
with open(file_url, "rt") as f:
|
||||
container.deserialize(f.read())
|
||||
except PermissionError:
|
||||
return { "status": "error", "message": "Permission denied when trying to read the file"}
|
||||
|
||||
container.setName(container_id)
|
||||
|
||||
UM.Settings.ContainerRegistry.getInstance().addContainer(container)
|
||||
|
||||
return { "status": "success", "message": "Successfully imported container {0}".format(container.getName()) }
|
||||
|
||||
def _updateContainerNameFilters(self):
|
||||
self._container_name_filters = {}
|
||||
for plugin_id, container_type in UM.Settings.ContainerRegistry.getContainerTypes():
|
||||
# Ignore default container types since those are not plugins
|
||||
if container_type in (UM.Settings.InstanceContainer, UM.Settings.ContainerStack, UM.Settings.DefinitionContainer):
|
||||
continue
|
||||
|
||||
serialize_type = ""
|
||||
try:
|
||||
plugin_metadata = UM.PluginRegistry.getInstance().getMetaData(plugin_id)
|
||||
if plugin_metadata:
|
||||
serialize_type = plugin_metadata["settings_container"]["type"]
|
||||
else:
|
||||
continue
|
||||
except KeyError as e:
|
||||
continue
|
||||
|
||||
mime_type = UM.Settings.ContainerRegistry.getMimeTypeForContainer(container_type)
|
||||
|
||||
entry = {
|
||||
"type": serialize_type,
|
||||
"mime": mime_type,
|
||||
"container": container_type
|
||||
}
|
||||
|
||||
suffix_list = "*." + mime_type.preferredSuffix
|
||||
for suffix in mime_type.suffixes:
|
||||
if suffix == mime_type.preferredSuffix:
|
||||
continue
|
||||
|
||||
suffix_list += ", *." + suffix
|
||||
|
||||
name_filter = "{0} ({1})".format(mime_type.comment, suffix_list)
|
||||
self._container_name_filters[name_filter] = entry
|
||||
|
||||
# Factory function, used by QML
|
||||
@staticmethod
|
||||
def createContainerManager(engine, js_engine):
|
||||
return ContainerManager()
|
|
@ -53,7 +53,7 @@ class CuraContainerRegistry(ContainerRegistry):
|
|||
def _containerExists(self, container_type, container_name):
|
||||
container_class = ContainerStack if container_type == "machine" else InstanceContainer
|
||||
|
||||
return self.findContainers(container_class, id = container_name, type = container_type) or \
|
||||
return self.findContainers(container_class, id = container_name, type = container_type, ignore_case = True) or \
|
||||
self.findContainers(container_class, name = container_name, type = container_type)
|
||||
|
||||
## Exports an profile to a file
|
|
@ -22,8 +22,9 @@ class ExtruderManager(QObject):
|
|||
def __init__(self, parent = None):
|
||||
super().__init__(parent)
|
||||
self._extruder_trains = { } #Per machine, a dictionary of extruder container stack IDs.
|
||||
self._active_extruder_index = 0
|
||||
UM.Application.getInstance().globalContainerStackChanged.connect(self._addCurrentMachineExtruders)
|
||||
self._active_extruder_index = -1
|
||||
UM.Application.getInstance().globalContainerStackChanged.connect(self.__globalContainerStackChanged)
|
||||
self._addCurrentMachineExtruders()
|
||||
|
||||
## Gets the unique identifier of the currently active extruder stack.
|
||||
#
|
||||
|
@ -66,6 +67,10 @@ class ExtruderManager(QObject):
|
|||
self._active_extruder_index = index
|
||||
self.activeExtruderChanged.emit()
|
||||
|
||||
@pyqtProperty(int, notify = activeExtruderChanged)
|
||||
def activeExtruderIndex(self):
|
||||
return self._active_extruder_index
|
||||
|
||||
def getActiveExtruderStack(self):
|
||||
global_container_stack = UM.Application.getInstance().getGlobalContainerStack()
|
||||
if global_container_stack:
|
||||
|
@ -74,20 +79,21 @@ class ExtruderManager(QObject):
|
|||
if global_definition_container.getId() in self._extruder_trains:
|
||||
if str(self._active_extruder_index) in self._extruder_trains[global_definition_container.getId()]:
|
||||
return self._extruder_trains[global_definition_container.getId()][str(self._active_extruder_index)]
|
||||
|
||||
return None
|
||||
|
||||
## Adds all extruders of a specific machine definition to the extruder
|
||||
# manager.
|
||||
#
|
||||
# \param machine_definition The machine to add the extruders for.
|
||||
def addMachineExtruders(self, machine_definition):
|
||||
changed = False
|
||||
machine_id = machine_definition.getId()
|
||||
if machine_id not in self._extruder_trains:
|
||||
self._extruder_trains[machine_id] = { }
|
||||
changed = True
|
||||
|
||||
container_registry = UM.Settings.ContainerRegistry.getInstance()
|
||||
if not container_registry: #Then we shouldn't have any machine definition either. In any case, there are no extruder trains then so bye bye.
|
||||
return
|
||||
if container_registry:
|
||||
|
||||
#Add the extruder trains that don't exist yet.
|
||||
for extruder_definition in container_registry.findDefinitionContainers(machine = machine_definition.getId()):
|
||||
|
@ -96,6 +102,7 @@ class ExtruderManager(QObject):
|
|||
UM.Logger.log("w", "Extruder definition %s specifies no position metadata entry.", extruder_definition.getId())
|
||||
if not container_registry.findContainerStacks(machine = machine_id, position = position): #Doesn't exist yet.
|
||||
self.createExtruderTrain(extruder_definition, machine_definition, position)
|
||||
changed = True
|
||||
|
||||
#Gets the extruder trains that we just created as well as any that still existed.
|
||||
extruder_trains = container_registry.findContainerStacks(type = "extruder_train", machine = machine_definition.getId())
|
||||
|
@ -104,8 +111,9 @@ class ExtruderManager(QObject):
|
|||
|
||||
#Ensure that the extruder train stacks are linked to global stack.
|
||||
extruder_train.setNextStack(UM.Application.getInstance().getGlobalContainerStack())
|
||||
changed = True
|
||||
|
||||
if extruder_trains:
|
||||
if changed:
|
||||
self.extrudersChanged.emit(machine_definition)
|
||||
|
||||
## Creates a container stack for an extruder train.
|
||||
|
@ -165,7 +173,16 @@ class ExtruderManager(QObject):
|
|||
material = materials[0]
|
||||
preferred_material_id = machine_definition.getMetaDataEntry("preferred_material")
|
||||
if preferred_material_id:
|
||||
preferred_materials = container_registry.findInstanceContainers(id = preferred_material_id, type = "material")
|
||||
search_criteria = { "type": "material", "id": preferred_material_id}
|
||||
if machine_definition.getMetaDataEntry("has_machine_materials"):
|
||||
search_criteria["definition"] = machine_definition.id
|
||||
|
||||
if machine_definition.getMetaDataEntry("has_variants") and variant:
|
||||
search_criteria["variant"] = variant.id
|
||||
else:
|
||||
search_criteria["definition"] = "fdmprinter"
|
||||
|
||||
preferred_materials = container_registry.findInstanceContainers(**search_criteria)
|
||||
if len(preferred_materials) >= 1:
|
||||
material = preferred_materials[0]
|
||||
else:
|
||||
|
@ -214,6 +231,10 @@ class ExtruderManager(QObject):
|
|||
for name in self._extruder_trains[machine_id]:
|
||||
yield self._extruder_trains[machine_id][name]
|
||||
|
||||
def __globalContainerStackChanged(self):
|
||||
self._addCurrentMachineExtruders()
|
||||
self.activeExtruderChanged.emit()
|
||||
|
||||
## Adds the extruders of the currently active machine.
|
||||
def _addCurrentMachineExtruders(self):
|
||||
global_stack = UM.Application.getInstance().getGlobalContainerStack()
|
|
@ -3,9 +3,10 @@
|
|||
|
||||
from PyQt5.QtCore import Qt, pyqtSignal, pyqtProperty
|
||||
|
||||
import cura.ExtruderManager
|
||||
import UM.Qt.ListModel
|
||||
|
||||
from . import ExtruderManager
|
||||
|
||||
## Model that holds extruders.
|
||||
#
|
||||
# This model is designed for use by any list of extruders, but specifically
|
||||
|
@ -49,9 +50,9 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
|
|||
self._active_extruder_stack = None
|
||||
|
||||
#Listen to changes.
|
||||
manager = cura.ExtruderManager.ExtruderManager.getInstance()
|
||||
manager = ExtruderManager.getInstance()
|
||||
manager.extrudersChanged.connect(self._updateExtruders) #When the list of extruders changes in general.
|
||||
UM.Application.getInstance().globalContainerStackChanged.connect(self._updateExtruders) #When the current machine changes.
|
||||
|
||||
self._updateExtruders()
|
||||
|
||||
manager.activeExtruderChanged.connect(self._onActiveExtruderChanged)
|
||||
|
@ -64,12 +65,13 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
|
|||
self.addGlobalChanged.emit()
|
||||
|
||||
addGlobalChanged = pyqtSignal()
|
||||
|
||||
@pyqtProperty(bool, fset = setAddGlobal, notify = addGlobalChanged)
|
||||
def addGlobal(self):
|
||||
return self._add_global
|
||||
|
||||
def _onActiveExtruderChanged(self):
|
||||
manager = cura.ExtruderManager.ExtruderManager.getInstance()
|
||||
manager = ExtruderManager.getInstance()
|
||||
active_extruder_stack = manager.getActiveExtruderStack()
|
||||
if self._active_extruder_stack != active_extruder_stack:
|
||||
if self._active_extruder_stack:
|
||||
|
@ -92,12 +94,14 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
|
|||
#
|
||||
# This should be called whenever the list of extruders changes.
|
||||
def _updateExtruders(self):
|
||||
self.clear()
|
||||
manager = cura.ExtruderManager.ExtruderManager.getInstance()
|
||||
global_container_stack = UM.Application.getInstance().getGlobalContainerStack()
|
||||
if not global_container_stack:
|
||||
return #There is no machine to get the extruders of.
|
||||
changed = False
|
||||
|
||||
if self.rowCount() != 0:
|
||||
self.clear()
|
||||
changed = True
|
||||
|
||||
global_container_stack = UM.Application.getInstance().getGlobalContainerStack()
|
||||
if global_container_stack:
|
||||
if self._add_global:
|
||||
material = global_container_stack.findContainer({ "type": "material" })
|
||||
colour = material.getMetaDataEntry("color_code", default = self.defaultColours[0]) if material else self.defaultColours[0]
|
||||
|
@ -108,7 +112,9 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
|
|||
"index": -1
|
||||
}
|
||||
self.appendItem(item)
|
||||
changed = True
|
||||
|
||||
manager = ExtruderManager.getInstance()
|
||||
for extruder in manager.getMachineExtruders(global_container_stack.getBottom().getId()):
|
||||
extruder_name = extruder.getName()
|
||||
material = extruder.findContainer({ "type": "material" })
|
||||
|
@ -119,7 +125,7 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
|
|||
position = int(position)
|
||||
except ValueError: #Not a proper int.
|
||||
position = -1
|
||||
default_colour = self.defaultColours[position] if position >= 0 and position < len(self.defaultColours) else defaultColours[0]
|
||||
default_colour = self.defaultColours[position] if position >= 0 and position < len(self.defaultColours) else self.defaultColours[0]
|
||||
colour = material.getMetaDataEntry("color_code", default = default_colour) if material else default_colour
|
||||
item = { #Construct an item with only the relevant information.
|
||||
"id": extruder.getId(),
|
||||
|
@ -128,6 +134,8 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
|
|||
"index": position
|
||||
}
|
||||
self.appendItem(item)
|
||||
changed = True
|
||||
|
||||
if changed:
|
||||
self.sort(lambda item: item["index"])
|
||||
self.modelChanged.emit()
|
|
@ -2,21 +2,23 @@
|
|||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from PyQt5.QtCore import QObject, pyqtSlot, pyqtProperty, pyqtSignal
|
||||
from PyQt5.QtWidgets import QMessageBox
|
||||
|
||||
from UM.Application import Application
|
||||
from UM.Preferences import Preferences
|
||||
from UM.Logger import Logger
|
||||
|
||||
import UM.Settings
|
||||
from UM.Settings.Validator import ValidatorState
|
||||
from UM.Settings.InstanceContainer import InstanceContainer
|
||||
|
||||
from cura.PrinterOutputDevice import PrinterOutputDevice
|
||||
from UM.Settings.ContainerStack import ContainerStack
|
||||
from . import ExtruderManager
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
import time
|
||||
|
||||
class MachineManagerModel(QObject):
|
||||
class MachineManager(QObject):
|
||||
def __init__(self, parent = None):
|
||||
super().__init__(parent)
|
||||
|
||||
|
@ -27,21 +29,20 @@ class MachineManagerModel(QObject):
|
|||
self._global_stack_valid = None
|
||||
self._onGlobalContainerChanged()
|
||||
|
||||
ExtruderManager.ExtruderManager.getInstance().activeExtruderChanged.connect(self._onActiveExtruderStackChanged)
|
||||
self.globalContainerChanged.connect(self._onActiveExtruderStackChanged)
|
||||
ExtruderManager.getInstance().activeExtruderChanged.connect(self._onActiveExtruderStackChanged)
|
||||
self._onActiveExtruderStackChanged()
|
||||
|
||||
## When the global container is changed, active material probably needs to be updated.
|
||||
self.globalContainerChanged.connect(self.activeMaterialChanged)
|
||||
self.globalContainerChanged.connect(self.activeVariantChanged)
|
||||
self.globalContainerChanged.connect(self.activeQualityChanged)
|
||||
ExtruderManager.ExtruderManager.getInstance().activeExtruderChanged.connect(self.activeMaterialChanged)
|
||||
ExtruderManager.ExtruderManager.getInstance().activeExtruderChanged.connect(self.activeVariantChanged)
|
||||
ExtruderManager.ExtruderManager.getInstance().activeExtruderChanged.connect(self.activeQualityChanged)
|
||||
ExtruderManager.getInstance().activeExtruderChanged.connect(self.activeMaterialChanged)
|
||||
ExtruderManager.getInstance().activeExtruderChanged.connect(self.activeVariantChanged)
|
||||
ExtruderManager.getInstance().activeExtruderChanged.connect(self.activeQualityChanged)
|
||||
|
||||
self.globalContainerChanged.connect(self.activeStackChanged)
|
||||
self.globalValueChanged.connect(self.activeStackChanged)
|
||||
ExtruderManager.ExtruderManager.getInstance().activeExtruderChanged.connect(self.activeStackChanged)
|
||||
ExtruderManager.getInstance().activeExtruderChanged.connect(self.activeStackChanged)
|
||||
|
||||
self._empty_variant_container = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = "empty_variant")[0]
|
||||
self._empty_material_container = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = "empty_material")[0]
|
||||
|
@ -51,6 +52,7 @@ class MachineManagerModel(QObject):
|
|||
|
||||
active_machine_id = Preferences.getInstance().getValue("cura/active_machine")
|
||||
|
||||
self._printer_output_devices = []
|
||||
Application.getInstance().getOutputDeviceManager().outputDevicesChanged.connect(self._onOutputDevicesChanged)
|
||||
|
||||
if active_machine_id != "":
|
||||
|
@ -58,6 +60,10 @@ class MachineManagerModel(QObject):
|
|||
self.setActiveMachine(active_machine_id)
|
||||
pass
|
||||
|
||||
self._auto_change_material_hotend_flood_window = 10 # The minimum number of seconds between asking if the material or hotend on the machine should be used
|
||||
self._auto_change_material_hotend_flood_time = 0 # The last timestamp (in seconds) when the user was asked about changing the material or hotend to whatis loaded on the machine
|
||||
self._auto_change_material_hotend_flood_last_choice = None # The last choice that was made, so we can apply that choice again
|
||||
|
||||
globalContainerChanged = pyqtSignal()
|
||||
activeMaterialChanged = pyqtSignal()
|
||||
activeVariantChanged = pyqtSignal()
|
||||
|
@ -72,15 +78,124 @@ class MachineManagerModel(QObject):
|
|||
outputDevicesChanged = pyqtSignal()
|
||||
|
||||
def _onOutputDevicesChanged(self):
|
||||
for printer_output_device in self._printer_output_devices:
|
||||
printer_output_device.hotendIdChanged.disconnect(self._onHotendIdChanged)
|
||||
printer_output_device.materialIdChanged.disconnect(self._onMaterialIdChanged)
|
||||
|
||||
self._printer_output_devices.clear()
|
||||
|
||||
for printer_output_device in Application.getInstance().getOutputDeviceManager().getOutputDevices():
|
||||
if isinstance(printer_output_device, PrinterOutputDevice):
|
||||
self._printer_output_devices.append(printer_output_device)
|
||||
printer_output_device.hotendIdChanged.connect(self._onHotendIdChanged)
|
||||
printer_output_device.materialIdChanged.connect(self._onMaterialIdChanged)
|
||||
|
||||
self.outputDevicesChanged.emit()
|
||||
|
||||
@pyqtProperty("QVariantList", notify = outputDevicesChanged)
|
||||
def printerOutputDevices(self):
|
||||
return self._printer_output_devices
|
||||
|
||||
def _onHotendIdChanged(self, index, hotend_id):
|
||||
if not self._global_container_stack:
|
||||
return
|
||||
|
||||
containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(type = "variant", definition = self._global_container_stack.getBottom().getId(), name = hotend_id)
|
||||
if containers:
|
||||
extruder_manager = ExtruderManager.getInstance()
|
||||
old_index = extruder_manager.activeExtruderIndex
|
||||
if old_index != index:
|
||||
extruder_manager.setActiveExtruderIndex(index)
|
||||
else:
|
||||
old_index = None
|
||||
|
||||
if self.activeVariantId != containers[0].getId():
|
||||
if time.time() - self._auto_change_material_hotend_flood_time > self._auto_change_material_hotend_flood_window:
|
||||
Application.getInstance().messageBox(catalog.i18nc("@window:title", "Changes on the Printer"), catalog.i18nc("@label", "Do you want to change the hotend to match the hotend in your printer?"),
|
||||
catalog.i18nc("@label", "The hotend on your printer was changed. For best results always slice for the hotend that is inserted in your printer."),
|
||||
buttons = QMessageBox.Yes + QMessageBox.No, icon = QMessageBox.Question, callback = self._hotendChangedDialogCallback, callback_arguments = [index, containers[0].getId()])
|
||||
else:
|
||||
self._hotendChangedDialogCallback(self._auto_change_material_hotend_flood_last_choice, index, containers[0].getId())
|
||||
if old_index is not None:
|
||||
extruder_manager.setActiveExtruderIndex(old_index)
|
||||
|
||||
else:
|
||||
Logger.log("w", "No variant found for printer definition %s with id %s" % (self._global_container_stack.getBottom().getId(), hotend_id))
|
||||
|
||||
def _hotendChangedDialogCallback(self, button, index, hotend_id):
|
||||
self._auto_change_material_hotend_flood_time = time.time()
|
||||
self._auto_change_material_hotend_flood_last_choice = button
|
||||
|
||||
Logger.log("d", "Setting hotend variant of hotend %d to %s" % (index, hotend_id))
|
||||
|
||||
extruder_manager = ExtruderManager.getInstance()
|
||||
old_index = extruder_manager.activeExtruderIndex
|
||||
if old_index != index:
|
||||
extruder_manager.setActiveExtruderIndex(index)
|
||||
else:
|
||||
old_index = None
|
||||
|
||||
self.setActiveVariant(hotend_id)
|
||||
|
||||
if old_index is not None:
|
||||
extruder_manager.setActiveExtruderIndex(old_index)
|
||||
|
||||
def _onMaterialIdChanged(self, index, material_id):
|
||||
if not self._global_container_stack:
|
||||
return
|
||||
|
||||
definition_id = "fdmprinter"
|
||||
if self._global_container_stack.getMetaDataEntry("has_machine_materials", False):
|
||||
definition_id = self._global_container_stack.getBottom().getId()
|
||||
|
||||
containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(type = "material", definition = definition_id, GUID = material_id)
|
||||
if containers:
|
||||
extruder_manager = ExtruderManager.getInstance()
|
||||
old_index = extruder_manager.activeExtruderIndex
|
||||
if old_index != index:
|
||||
extruder_manager.setActiveExtruderIndex(index)
|
||||
else:
|
||||
old_index = None
|
||||
|
||||
if self.activeMaterialId != containers[0].getId():
|
||||
if time.time() - self._auto_change_material_hotend_flood_time > self._auto_change_material_hotend_flood_window:
|
||||
Application.getInstance().messageBox(catalog.i18nc("@window:title", "Changes on the Printer"), catalog.i18nc("@label", "Do you want to change the material to match the material in your printer?"),
|
||||
catalog.i18nc("@label", "The material on your printer was changed. For best results always slice for the material that is inserted in your printer."),
|
||||
buttons = QMessageBox.Yes + QMessageBox.No, icon = QMessageBox.Question, callback = self._materialIdChangedDialogCallback, callback_arguments = [index, containers[0].getId()])
|
||||
else:
|
||||
self._materialIdChangedDialogCallback(self._auto_change_material_hotend_flood_last_choice, index, containers[0].getId())
|
||||
if old_index is not None:
|
||||
extruder_manager.setActiveExtruderIndex(old_index)
|
||||
|
||||
else:
|
||||
Logger.log("w", "No material definition found for printer definition %s and GUID %s" % (definition_id, material_id))
|
||||
|
||||
def _materialIdChangedDialogCallback(self, button, index, material_id):
|
||||
self._auto_change_material_hotend_flood_time = time.time()
|
||||
self._auto_change_material_hotend_flood_last_choice = button
|
||||
|
||||
Logger.log("d", "Setting material of hotend %d to %s" % (index, material_id))
|
||||
|
||||
extruder_manager = ExtruderManager.getInstance()
|
||||
old_index = extruder_manager.activeExtruderIndex
|
||||
if old_index != index:
|
||||
extruder_manager.setActiveExtruderIndex(index)
|
||||
else:
|
||||
old_index = None
|
||||
|
||||
self.setActiveMaterial(material_id)
|
||||
|
||||
if old_index is not None:
|
||||
extruder_manager.setActiveExtruderIndex(old_index)
|
||||
|
||||
|
||||
def _onGlobalPropertyChanged(self, key, property_name):
|
||||
if property_name == "value":
|
||||
self.globalValueChanged.emit()
|
||||
if property_name == "validationState":
|
||||
if self._global_stack_valid:
|
||||
changed_validation_state = self._active_container_stack.getProperty(key, property_name)
|
||||
if changed_validation_state in (ValidatorState.Exception, ValidatorState.MaximumError, ValidatorState.MinimumError):
|
||||
if changed_validation_state in (UM.Settings.ValidatorState.Exception, UM.Settings.ValidatorState.MaximumError, UM.Settings.ValidatorState.MinimumError):
|
||||
self._global_stack_valid = False
|
||||
self.globalValidationChanged.emit()
|
||||
else:
|
||||
|
@ -91,25 +206,41 @@ class MachineManagerModel(QObject):
|
|||
|
||||
def _onGlobalContainerChanged(self):
|
||||
if self._global_container_stack:
|
||||
self._global_container_stack.nameChanged.disconnect(self._onMachineNameChanged)
|
||||
self._global_container_stack.containersChanged.disconnect(self._onInstanceContainersChanged)
|
||||
self._global_container_stack.propertyChanged.disconnect(self._onGlobalPropertyChanged)
|
||||
|
||||
material = self._global_container_stack.findContainer({"type": "material"})
|
||||
material.nameChanged.disconnect(self._onMaterialNameChanged)
|
||||
|
||||
quality = self._global_container_stack.findContainer({"type": "quality"})
|
||||
quality.nameChanged.disconnect(self._onQualityNameChanged)
|
||||
|
||||
self._global_container_stack = Application.getInstance().getGlobalContainerStack()
|
||||
self._active_container_stack = self._global_container_stack
|
||||
|
||||
self.globalContainerChanged.emit()
|
||||
|
||||
if self._global_container_stack:
|
||||
Preferences.getInstance().setValue("cura/active_machine", self._global_container_stack.getId())
|
||||
self._global_container_stack.nameChanged.connect(self._onMachineNameChanged)
|
||||
self._global_container_stack.containersChanged.connect(self._onInstanceContainersChanged)
|
||||
self._global_container_stack.propertyChanged.connect(self._onGlobalPropertyChanged)
|
||||
self._global_stack_valid = not self._checkStackForErrors(self._global_container_stack)
|
||||
|
||||
material = self._global_container_stack.findContainer({"type": "material"})
|
||||
material.nameChanged.connect(self._onMaterialNameChanged)
|
||||
|
||||
quality = self._global_container_stack.findContainer({"type": "quality"})
|
||||
quality.nameChanged.connect(self._onQualityNameChanged)
|
||||
|
||||
def _onActiveExtruderStackChanged(self):
|
||||
self.blurSettings.emit() # Ensure no-one has focus.
|
||||
if self._active_container_stack and self._active_container_stack != self._global_container_stack:
|
||||
self._active_container_stack.containersChanged.disconnect(self._onInstanceContainersChanged)
|
||||
self._active_container_stack.propertyChanged.disconnect(self._onGlobalPropertyChanged)
|
||||
|
||||
self._active_container_stack = ExtruderManager.ExtruderManager.getInstance().getActiveExtruderStack()
|
||||
self._active_container_stack = ExtruderManager.getInstance().getActiveExtruderStack()
|
||||
if self._active_container_stack:
|
||||
self._active_container_stack.containersChanged.connect(self._onInstanceContainersChanged)
|
||||
self._active_container_stack.propertyChanged.connect(self._onGlobalPropertyChanged)
|
||||
|
@ -161,13 +292,10 @@ class MachineManagerModel(QObject):
|
|||
new_global_stack.addContainer(quality_instance_container)
|
||||
new_global_stack.addContainer(current_settings_instance_container)
|
||||
|
||||
ExtruderManager.ExtruderManager.getInstance().addMachineExtruders(definition)
|
||||
ExtruderManager.getInstance().addMachineExtruders(definition)
|
||||
|
||||
Application.getInstance().setGlobalContainerStack(new_global_stack)
|
||||
|
||||
@pyqtProperty("QVariantList", notify = outputDevicesChanged)
|
||||
def printerOutputDevices(self):
|
||||
return [printer_output_device for printer_output_device in Application.getInstance().getOutputDeviceManager().getOutputDevices() if isinstance(printer_output_device, PrinterOutputDevice)]
|
||||
|
||||
## Create a name that is not empty and unique
|
||||
# \param container_type \type{string} Type of the container (machine, quality, ...)
|
||||
|
@ -185,7 +313,7 @@ class MachineManagerModel(QObject):
|
|||
|
||||
for key in stack.getAllKeys():
|
||||
validation_state = stack.getProperty(key, "validationState")
|
||||
if validation_state in (ValidatorState.Exception, ValidatorState.MaximumError, ValidatorState.MinimumError):
|
||||
if validation_state in (UM.Settings.ValidatorState.Exception, UM.Settings.ValidatorState.MaximumError, UM.Settings.ValidatorState.MinimumError):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
@ -213,7 +341,7 @@ class MachineManagerModel(QObject):
|
|||
# Calling _checkStackForErrors on every change is simply too expensive
|
||||
@pyqtProperty(bool, notify = globalValidationChanged)
|
||||
def isGlobalStackValid(self):
|
||||
return self._global_stack_valid
|
||||
return bool(self._global_stack_valid)
|
||||
|
||||
@pyqtProperty(str, notify = activeStackChanged)
|
||||
def activeUserProfileId(self):
|
||||
|
@ -236,6 +364,13 @@ class MachineManagerModel(QObject):
|
|||
|
||||
return ""
|
||||
|
||||
@pyqtProperty(str, notify = activeStackChanged)
|
||||
def activeStackId(self):
|
||||
if self._active_container_stack:
|
||||
return self._active_container_stack.getId()
|
||||
|
||||
return ""
|
||||
|
||||
@pyqtProperty(str, notify = activeMaterialChanged)
|
||||
def activeMaterialName(self):
|
||||
if self._active_container_stack:
|
||||
|
@ -284,8 +419,8 @@ class MachineManagerModel(QObject):
|
|||
if new_container_id == "":
|
||||
return
|
||||
self.blurSettings.emit()
|
||||
self.updateQualityContainerFromUserContainer(new_container_id)
|
||||
self.setActiveQuality(new_container_id)
|
||||
self.updateQualityContainerFromUserContainer()
|
||||
return new_container_id
|
||||
|
||||
@pyqtSlot(str, result=str)
|
||||
|
@ -296,7 +431,7 @@ class MachineManagerModel(QObject):
|
|||
if containers:
|
||||
new_name = self._createUniqueName("quality", "", containers[0].getName(), catalog.i18nc("@label", "Custom profile"))
|
||||
|
||||
new_container = InstanceContainer("")
|
||||
new_container = UM.Settings.InstanceContainer("")
|
||||
|
||||
## Copy all values
|
||||
new_container.deserialize(containers[0].serialize())
|
||||
|
@ -323,7 +458,7 @@ class MachineManagerModel(QObject):
|
|||
# As we also want the id of the container to be changed (so that profile name is the name of the file
|
||||
# on disk. We need to create a new instance and remove it (so the old file of the container is removed)
|
||||
# If we don't do that, we might get duplicates & other weird issues.
|
||||
new_container = InstanceContainer("")
|
||||
new_container = UM.Settings.InstanceContainer("")
|
||||
new_container.deserialize(containers[0].serialize())
|
||||
|
||||
# Actually set the name
|
||||
|
@ -358,12 +493,23 @@ class MachineManagerModel(QObject):
|
|||
self.setActiveQuality(containers[0].getId())
|
||||
self.activeQualityChanged.emit()
|
||||
|
||||
@pyqtSlot()
|
||||
def updateQualityContainerFromUserContainer(self):
|
||||
@pyqtSlot(str)
|
||||
def updateQualityContainerFromUserContainer(self, quality_id = None):
|
||||
if not self._active_container_stack:
|
||||
return
|
||||
user_settings = self._active_container_stack.getTop()
|
||||
|
||||
if quality_id:
|
||||
quality = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = quality_id, type = "quality")
|
||||
if quality:
|
||||
quality = quality[0]
|
||||
else:
|
||||
quality = self._active_container_stack.findContainer({"type": "quality"})
|
||||
|
||||
if not quality:
|
||||
return
|
||||
|
||||
user_settings = self._active_container_stack.getTop()
|
||||
|
||||
for key in user_settings.getAllKeys():
|
||||
quality.setProperty(key, "value", user_settings.getProperty(key, "value"))
|
||||
self.clearUserSettings() # As all users settings are noq a quality, remove them.
|
||||
|
@ -378,21 +524,26 @@ class MachineManagerModel(QObject):
|
|||
old_material = self._active_container_stack.findContainer({"type":"material"})
|
||||
old_quality = self._active_container_stack.findContainer({"type": "quality"})
|
||||
if old_material:
|
||||
old_material.nameChanged.disconnect(self._onMaterialNameChanged)
|
||||
|
||||
material_index = self._active_container_stack.getContainerIndex(old_material)
|
||||
self._active_container_stack.replaceContainer(material_index, containers[0])
|
||||
|
||||
containers[0].nameChanged.connect(self._onMaterialNameChanged)
|
||||
|
||||
preferred_quality_name = None
|
||||
if old_quality:
|
||||
preferred_quality_name = old_quality.getName()
|
||||
|
||||
self.setActiveQuality(self._updateQualityContainer(self._global_container_stack.getBottom(), containers[0], preferred_quality_name).id)
|
||||
else:
|
||||
Logger.log("w", "While trying to set the active material, no material was found to replace.")
|
||||
|
||||
@pyqtSlot(str)
|
||||
def setActiveVariant(self, variant_id):
|
||||
containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = variant_id)
|
||||
if not containers or not self._active_container_stack:
|
||||
return
|
||||
|
||||
old_variant = self._active_container_stack.findContainer({"type": "variant"})
|
||||
old_material = self._active_container_stack.findContainer({"type": "material"})
|
||||
if old_variant:
|
||||
|
@ -401,9 +552,10 @@ class MachineManagerModel(QObject):
|
|||
|
||||
preferred_material = None
|
||||
if old_material:
|
||||
preferred_material = old_material.getId()
|
||||
|
||||
self.setActiveMaterial(self._updateMaterialContainer(self._global_container_stack.getBottom(), containers[0], preferred_material).id)
|
||||
preferred_material_name = old_material.getName()
|
||||
self.setActiveMaterial(self._updateMaterialContainer(self._global_container_stack.getBottom(), containers[0], preferred_material_name).id)
|
||||
else:
|
||||
Logger.log("w", "While trying to set the active variant, no variant was found to replace.")
|
||||
|
||||
@pyqtSlot(str)
|
||||
def setActiveQuality(self, quality_id):
|
||||
|
@ -412,10 +564,37 @@ class MachineManagerModel(QObject):
|
|||
return
|
||||
|
||||
old_quality = self._active_container_stack.findContainer({"type": "quality"})
|
||||
if old_quality:
|
||||
if old_quality and old_quality != containers[0]:
|
||||
old_quality.nameChanged.disconnect(self._onQualityNameChanged)
|
||||
|
||||
quality_index = self._active_container_stack.getContainerIndex(old_quality)
|
||||
|
||||
self._active_container_stack.replaceContainer(quality_index, containers[0])
|
||||
|
||||
containers[0].nameChanged.connect(self._onQualityNameChanged)
|
||||
|
||||
if self.hasUserSettings and Preferences.getInstance().getValue("cura/active_mode") == 1:
|
||||
# Ask the user if the user profile should be cleared or not (discarding the current settings)
|
||||
# In Simple Mode we assume the user always wants to keep the (limited) current settings
|
||||
details = catalog.i18nc("@label", "You made changes to the following setting(s):")
|
||||
user_settings = self._active_container_stack.getTop().findInstances(**{})
|
||||
for setting in user_settings:
|
||||
details = details + "\n " + setting.definition.label
|
||||
|
||||
Application.getInstance().messageBox(catalog.i18nc("@window:title", "Switched profiles"), catalog.i18nc("@label", "Do you want to transfer your changed settings to this profile?"),
|
||||
catalog.i18nc("@label", "If you transfer your settings they will override settings in the profile."), details,
|
||||
buttons = QMessageBox.Yes + QMessageBox.No, icon = QMessageBox.Question, callback = self._keepUserSettingsDialogCallback)
|
||||
else:
|
||||
Logger.log("w", "While trying to set the active quality, no quality was found to replace.")
|
||||
|
||||
def _keepUserSettingsDialogCallback(self, button):
|
||||
if button == QMessageBox.Yes:
|
||||
# Yes, keep the settings in the user profile with this profile
|
||||
pass
|
||||
elif button == QMessageBox.No:
|
||||
# No, discard the settings in the user profile
|
||||
self.clearUserSettings()
|
||||
|
||||
@pyqtProperty(str, notify = activeVariantChanged)
|
||||
def activeVariantName(self):
|
||||
if self._active_container_stack:
|
||||
|
@ -453,7 +632,7 @@ class MachineManagerModel(QObject):
|
|||
|
||||
@pyqtSlot(str)
|
||||
def removeMachine(self, machine_id):
|
||||
# If the machine that is being removed is the currently active machine, set another machine as the active machine
|
||||
# If the machine that is being removed is the currently active machine, set another machine as the active machine.
|
||||
activate_new_machine = (self._global_container_stack and self._global_container_stack.getId() == machine_id)
|
||||
|
||||
current_settings_id = machine_id + "_current_settings"
|
||||
|
@ -482,6 +661,8 @@ class MachineManagerModel(QObject):
|
|||
|
||||
return False
|
||||
|
||||
## Property to indicate if a machine has "specialized" material profiles.
|
||||
# Some machines have their own material profiles that "override" the default catch all profiles.
|
||||
@pyqtProperty(bool, notify = globalContainerChanged)
|
||||
def filterMaterialsByMachine(self):
|
||||
if self._global_container_stack:
|
||||
|
@ -489,19 +670,27 @@ class MachineManagerModel(QObject):
|
|||
|
||||
return False
|
||||
|
||||
## Property to indicate if a machine has "specialized" quality profiles.
|
||||
# Some machines have their own quality profiles that "override" the default catch all profiles.
|
||||
@pyqtProperty(bool, notify = globalContainerChanged)
|
||||
def filterQualityByMachine(self):
|
||||
if self._global_container_stack:
|
||||
return bool(self._global_container_stack.getMetaDataEntry("has_machine_quality", False))
|
||||
|
||||
return False
|
||||
|
||||
## Get the Definition ID of a machine (specified by ID)
|
||||
# \param machine_id string machine id to get the definition ID of
|
||||
# \returns DefinitionID (string) if found, None otherwise
|
||||
@pyqtSlot(str, result = str)
|
||||
def getDefinitionByMachineId(self, machine_id):
|
||||
containers = UM.Settings.ContainerRegistry.getInstance().findContainerStacks(id=machine_id)
|
||||
if containers:
|
||||
return containers[0].getBottom().getId()
|
||||
|
||||
@staticmethod
|
||||
def createMachineManager(engine, script_engine):
|
||||
return MachineManager()
|
||||
|
||||
def _updateVariantContainer(self, definition):
|
||||
if not definition.getMetaDataEntry("has_variants"):
|
||||
return self._empty_variant_container
|
||||
|
@ -519,7 +708,7 @@ class MachineManagerModel(QObject):
|
|||
|
||||
return self._empty_variant_container
|
||||
|
||||
def _updateMaterialContainer(self, definition, variant_container = None, preferred_material = None):
|
||||
def _updateMaterialContainer(self, definition, variant_container = None, preferred_material_name = None):
|
||||
if not definition.getMetaDataEntry("has_materials"):
|
||||
return self._empty_material_container
|
||||
|
||||
|
@ -533,7 +722,9 @@ class MachineManagerModel(QObject):
|
|||
else:
|
||||
search_criteria["definition"] = "fdmprinter"
|
||||
|
||||
if not preferred_material:
|
||||
if preferred_material_name:
|
||||
search_criteria["name"] = preferred_material_name
|
||||
else:
|
||||
preferred_material = definition.getMetaDataEntry("preferred_material")
|
||||
if preferred_material:
|
||||
search_criteria["id"] = preferred_material
|
||||
|
@ -542,6 +733,15 @@ class MachineManagerModel(QObject):
|
|||
if containers:
|
||||
return containers[0]
|
||||
|
||||
if "name" in search_criteria or "id" in search_criteria:
|
||||
# If a material by this name can not be found, try a wider set of search criteria
|
||||
search_criteria.pop("name", None)
|
||||
search_criteria.pop("id", None)
|
||||
|
||||
containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(**search_criteria)
|
||||
if containers:
|
||||
return containers[0]
|
||||
|
||||
return self._empty_material_container
|
||||
|
||||
def _updateQualityContainer(self, definition, material_container = None, preferred_quality_name = None):
|
||||
|
@ -566,7 +766,25 @@ class MachineManagerModel(QObject):
|
|||
if containers:
|
||||
return containers[0]
|
||||
|
||||
if "name" in search_criteria or "id" in search_criteria:
|
||||
# If a quality by this name can not be found, try a wider set of search criteria
|
||||
search_criteria.pop("name", None)
|
||||
search_criteria.pop("id", None)
|
||||
|
||||
containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(**search_criteria)
|
||||
if containers:
|
||||
return containers[0]
|
||||
|
||||
return self._empty_quality_container
|
||||
|
||||
def createMachineManagerModel(engine, script_engine):
|
||||
return MachineManagerModel()
|
||||
def _onMachineNameChanged(self):
|
||||
print("machine name changed")
|
||||
self.globalContainerChanged.emit()
|
||||
|
||||
def _onMaterialNameChanged(self):
|
||||
print("material name changed")
|
||||
self.activeMaterialChanged.emit()
|
||||
|
||||
def _onQualityNameChanged(self):
|
||||
print("quality name changed")
|
||||
self.activeQualityChanged.emit()
|
19
cura/Settings/MaterialSettingsVisibilityHandler.py
Normal file
19
cura/Settings/MaterialSettingsVisibilityHandler.py
Normal file
|
@ -0,0 +1,19 @@
|
|||
# Copyright (c) 2016 Ultimaker B.V.
|
||||
# Uranium is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
import UM.Settings.Models
|
||||
|
||||
class MaterialSettingsVisibilityHandler(UM.Settings.Models.SettingVisibilityHandler):
|
||||
def __init__(self, parent = None, *args, **kwargs):
|
||||
super().__init__(parent = parent, *args, **kwargs)
|
||||
|
||||
material_settings = set([
|
||||
"material_print_temperature",
|
||||
"material_bed_temperature",
|
||||
"material_standby_temperature",
|
||||
"cool_fan_speed",
|
||||
"retraction_amount",
|
||||
"retraction_speed",
|
||||
])
|
||||
|
||||
self.setVisible(material_settings)
|
12
cura/Settings/__init__.py
Normal file
12
cura/Settings/__init__.py
Normal file
|
@ -0,0 +1,12 @@
|
|||
# Copyright (c) 2016 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from .MaterialSettingsVisibilityHandler import MaterialSettingsVisibilityHandler
|
||||
from .ContainerManager import ContainerManager
|
||||
from .ContainerSettingsModel import ContainerSettingsModel
|
||||
from .CuraContainerRegistry import CuraContainerRegistry
|
||||
from .ExtruderManager import ExtruderManager
|
||||
from .ExtrudersModel import ExtrudersModel
|
||||
from .MachineManager import MachineManager
|
||||
from .MaterialSettingsVisibilityHandler import MaterialSettingsVisibilityHandler
|
||||
from .SettingOverrideDecorator import SettingOverrideDecorator
|
|
@ -10,3 +10,8 @@ class ZOffsetDecorator(SceneNodeDecorator):
|
|||
|
||||
def getZOffset(self):
|
||||
return self._z_offset
|
||||
|
||||
def __deepcopy__(self, memo):
|
||||
copied_decorator = ZOffsetDecorator()
|
||||
copied_decorator.setZOffset(self.getZOffset())
|
||||
return copied_decorator
|
||||
|
|
|
@ -35,7 +35,7 @@ sys.excepthook = exceptHook
|
|||
import Arcus #@UnusedImport
|
||||
from UM.Platform import Platform
|
||||
import cura.CuraApplication
|
||||
import cura.CuraContainerRegistry
|
||||
import cura.Settings.CuraContainerRegistry
|
||||
|
||||
if Platform.isWindows() and hasattr(sys, "frozen"):
|
||||
dirpath = os.path.expanduser("~/AppData/Local/cura/")
|
||||
|
@ -44,7 +44,7 @@ if Platform.isWindows() and hasattr(sys, "frozen"):
|
|||
sys.stderr = open(os.path.join(dirpath, "stderr.log"), "w")
|
||||
|
||||
# Force an instance of CuraContainerRegistry to be created and reused later.
|
||||
cura.CuraContainerRegistry.CuraContainerRegistry.getInstance()
|
||||
cura.Settings.CuraContainerRegistry.getInstance()
|
||||
|
||||
app = cura.CuraApplication.CuraApplication.getInstance()
|
||||
app.run()
|
||||
|
|
|
@ -53,7 +53,6 @@ class ThreeMFReader(MeshReader):
|
|||
triangles = entry.findall(".//3mf:triangle", self._namespaces)
|
||||
mesh_builder.reserveFaceCount(len(triangles))
|
||||
|
||||
#for triangle in object.mesh.triangles.triangle:
|
||||
for triangle in triangles:
|
||||
v1 = int(triangle.get("v1"))
|
||||
v2 = int(triangle.get("v2"))
|
||||
|
@ -67,11 +66,11 @@ class ThreeMFReader(MeshReader):
|
|||
|
||||
# Rotate the model; We use a different coordinate frame.
|
||||
rotation = Matrix()
|
||||
rotation.setByRotationAxis(-0.5 * math.pi, Vector(1,0,0))
|
||||
rotation.setByRotationAxis(-0.5 * math.pi, Vector(1, 0, 0))
|
||||
|
||||
#TODO: We currently do not check for normals and simply recalculate them.
|
||||
# TODO: We currently do not check for normals and simply recalculate them.
|
||||
mesh_builder.calculateNormals()
|
||||
|
||||
mesh_builder.setFileName(file_name)
|
||||
node.setMeshData(mesh_builder.build().getTransformed(rotation))
|
||||
node.setSelectable(True)
|
||||
|
||||
|
@ -108,11 +107,11 @@ class ThreeMFReader(MeshReader):
|
|||
|
||||
Job.yieldThread()
|
||||
|
||||
#If there is more then one object, group them.
|
||||
# If there is more then one object, group them.
|
||||
if len(objects) > 1:
|
||||
group_decorator = GroupDecorator()
|
||||
result.addDecorator(group_decorator)
|
||||
except Exception as e:
|
||||
Logger.log("e" ,"exception occured in 3mf reader: %s" , e)
|
||||
Logger.log("e", "exception occured in 3mf reader: %s", e)
|
||||
|
||||
return result
|
||||
|
|
|
@ -82,10 +82,15 @@ message GCodeLayer {
|
|||
bytes data = 2;
|
||||
}
|
||||
|
||||
message ObjectPrintTime { // The print time for the whole print and material estimates for the first extruder
|
||||
|
||||
message PrintTimeMaterialEstimates { // The print time for the whole print and material estimates for the extruder
|
||||
float time = 1; // Total time estimate
|
||||
repeated MaterialEstimates materialEstimates = 2; // materialEstimates data
|
||||
}
|
||||
|
||||
message MaterialEstimates {
|
||||
int64 id = 1;
|
||||
float time = 2; // Total time estimate
|
||||
float material_amount = 3; // material used in the first extruder
|
||||
float material_amount = 2; // material used in the extruder
|
||||
}
|
||||
|
||||
message SettingList {
|
||||
|
|
|
@ -13,7 +13,7 @@ from UM.Resources import Resources
|
|||
from UM.Settings.Validator import ValidatorState #To find if a setting is in an error state. We can't slice then.
|
||||
from UM.Platform import Platform
|
||||
|
||||
from cura.ExtruderManager import ExtruderManager
|
||||
import cura.Settings
|
||||
|
||||
from cura.OneAtATimeIterator import OneAtATimeIterator
|
||||
from . import ProcessSlicedLayersJob
|
||||
|
@ -64,7 +64,7 @@ class CuraEngineBackend(Backend):
|
|||
self._onGlobalStackChanged()
|
||||
|
||||
self._active_extruder_stack = None
|
||||
ExtruderManager.getInstance().activeExtruderChanged.connect(self._onActiveExtruderChanged)
|
||||
cura.Settings.ExtruderManager.getInstance().activeExtruderChanged.connect(self._onActiveExtruderChanged)
|
||||
self._onActiveExtruderChanged()
|
||||
|
||||
#When you update a setting and other settings get changed through inheritance, many propertyChanged signals are fired.
|
||||
|
@ -81,7 +81,7 @@ class CuraEngineBackend(Backend):
|
|||
self._message_handlers["cura.proto.Progress"] = self._onProgressMessage
|
||||
self._message_handlers["cura.proto.GCodeLayer"] = self._onGCodeLayerMessage
|
||||
self._message_handlers["cura.proto.GCodePrefix"] = self._onGCodePrefixMessage
|
||||
self._message_handlers["cura.proto.ObjectPrintTime"] = self._onObjectPrintTimeMessage
|
||||
self._message_handlers["cura.proto.PrintTimeMaterialEstimates"] = self._onPrintTimeMaterialEstimates
|
||||
self._message_handlers["cura.proto.SlicingFinished"] = self._onSlicingFinishedMessage
|
||||
|
||||
self._start_slice_job = None
|
||||
|
@ -128,12 +128,16 @@ class CuraEngineBackend(Backend):
|
|||
|
||||
## Perform a slice of the scene.
|
||||
def slice(self):
|
||||
if not self._enabled or not self._global_container_stack: #We shouldn't be slicing.
|
||||
# try again in a short time
|
||||
self._change_timer.start()
|
||||
return
|
||||
|
||||
self.printDurationMessage.emit(0, [0])
|
||||
|
||||
self._stored_layer_data = []
|
||||
self._stored_optimized_layer_data = []
|
||||
|
||||
if not self._enabled or not self._global_container_stack: #We shouldn't be slicing.
|
||||
return
|
||||
|
||||
if self._slicing: #We were already slicing. Stop the old job.
|
||||
self._terminate()
|
||||
|
||||
|
@ -304,9 +308,12 @@ class CuraEngineBackend(Backend):
|
|||
## Called when a print time message is received from the engine.
|
||||
#
|
||||
# \param message The protobuf message containing the print time and
|
||||
# material amount.
|
||||
def _onObjectPrintTimeMessage(self, message):
|
||||
self.printDurationMessage.emit(message.time, message.material_amount)
|
||||
# material amount per extruder
|
||||
def _onPrintTimeMaterialEstimates(self, message):
|
||||
material_amounts = []
|
||||
for index in range(message.repeatedMessageCount("materialEstimates")):
|
||||
material_amounts.append(message.getRepeatedMessage("materialEstimates", index).material_amount)
|
||||
self.printDurationMessage.emit(message.time, material_amounts)
|
||||
|
||||
## Creates a new socket connection.
|
||||
def _createSocket(self):
|
||||
|
@ -389,8 +396,8 @@ class CuraEngineBackend(Backend):
|
|||
self._active_extruder_stack.propertyChanged.disconnect(self._onSettingChanged)
|
||||
self._active_extruder_stack.containersChanged.disconnect(self._onChanged)
|
||||
|
||||
self._active_extruder_stack = ExtruderManager.getInstance().getActiveExtruderStack()
|
||||
self._active_extruder_stack = cura.Settings.ExtruderManager.getInstance().getActiveExtruderStack()
|
||||
if self._active_extruder_stack:
|
||||
self._active_extruder_stack.propertyChanged.connect(self._onSettingChanged) # Note: Only starts slicing when the value changed.
|
||||
self._active_extruder_stack.containersChanged.connect(self._onChanged)
|
||||
self._onChanged()
|
||||
|
||||
|
|
|
@ -15,7 +15,8 @@ from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
|||
from UM.Settings.Validator import ValidatorState
|
||||
|
||||
from cura.OneAtATimeIterator import OneAtATimeIterator
|
||||
from cura.ExtruderManager import ExtruderManager
|
||||
|
||||
import cura.Settings
|
||||
|
||||
class StartJobResult(IntEnum):
|
||||
Finished = 1
|
||||
|
@ -128,7 +129,7 @@ class StartSliceJob(Job):
|
|||
|
||||
self._buildGlobalSettingsMessage(stack)
|
||||
|
||||
for extruder_stack in ExtruderManager.getInstance().getMachineExtruders(stack.getBottom().getId()):
|
||||
for extruder_stack in cura.Settings.ExtruderManager.getInstance().getMachineExtruders(stack.getBottom().getId()):
|
||||
self._buildExtruderMessage(extruder_stack)
|
||||
|
||||
for group in object_groups:
|
||||
|
|
|
@ -22,7 +22,7 @@ class GCodeProfileReader(ProfileReader):
|
|||
# It can only read settings with the same version as the version it was
|
||||
# written with. If the file format is changed in a way that breaks reverse
|
||||
# compatibility, increment this version number!
|
||||
version = 1
|
||||
version = 2
|
||||
|
||||
## Dictionary that defines how characters are escaped when embedded in
|
||||
# g-code.
|
||||
|
@ -73,18 +73,14 @@ class GCodeProfileReader(ProfileReader):
|
|||
serialized = pattern.sub(lambda m: GCodeProfileReader.escape_characters[re.escape(m.group(0))], serialized)
|
||||
Logger.log("i", "Serialized the following from %s: %s" %(file_name, repr(serialized)))
|
||||
|
||||
# Create an empty profile - the id will be changed later
|
||||
# Create an empty profile - the id and name will be changed by the ContainerRegistry
|
||||
profile = InstanceContainer("")
|
||||
profile.addMetaDataEntry("type", "quality")
|
||||
try:
|
||||
profile.deserialize(serialized)
|
||||
except Exception as e: # Not a valid g-code file.
|
||||
Logger.log("e", "Unable to serialise the profile: %s", str(e))
|
||||
return None
|
||||
|
||||
#Creating a unique name using the filename of the GCode
|
||||
new_name = catalog.i18nc("@label", "Custom profile (%s)") %(os.path.splitext(os.path.basename(file_name))[0])
|
||||
profile.setName(new_name)
|
||||
profile._id = new_name
|
||||
profile.addMetaDataEntry("type", "quality")
|
||||
|
||||
return profile
|
||||
|
|
|
@ -23,7 +23,7 @@ class GCodeWriter(MeshWriter):
|
|||
# It can only read settings with the same version as the version it was
|
||||
# written with. If the file format is changed in a way that breaks reverse
|
||||
# compatibility, increment this version number!
|
||||
version = 1
|
||||
version = 2
|
||||
|
||||
## Dictionary that defines how characters are escaped when embedded in
|
||||
# g-code.
|
||||
|
@ -68,23 +68,21 @@ class GCodeWriter(MeshWriter):
|
|||
prefix = ";SETTING_" + str(GCodeWriter.version) + " " # The prefix to put before each line.
|
||||
prefix_length = len(prefix)
|
||||
|
||||
all_settings = InstanceContainer("G-code-imported-profile") #Create a new 'profile' with ALL settings so that the slice can be precisely reproduced.
|
||||
all_settings.setDefinition(settings.getBottom())
|
||||
for key in settings.getAllKeys():
|
||||
all_settings.setProperty(key, "value", settings.getProperty(key, "value")) #Just copy everything over to the setting instance.
|
||||
serialised = all_settings.serialize()
|
||||
global_stack = Application.getInstance().getGlobalContainerStack()
|
||||
container_with_profile = global_stack.findContainer({"type": "quality"})
|
||||
serialized = container_with_profile.serialize()
|
||||
|
||||
# Escape characters that have a special meaning in g-code comments.
|
||||
pattern = re.compile("|".join(GCodeWriter.escape_characters.keys()))
|
||||
# Perform the replacement with a regular expression.
|
||||
serialised = pattern.sub(lambda m: GCodeWriter.escape_characters[re.escape(m.group(0))], serialised)
|
||||
serialized = pattern.sub(lambda m: GCodeWriter.escape_characters[re.escape(m.group(0))], serialized)
|
||||
|
||||
# Introduce line breaks so that each comment is no longer than 80 characters. Prepend each line with the prefix.
|
||||
result = ""
|
||||
|
||||
# Lines have 80 characters, so the payload of each line is 80 - prefix.
|
||||
for pos in range(0, len(serialised), 80 - prefix_length):
|
||||
result += prefix + serialised[pos : pos + 80 - prefix_length] + "\n"
|
||||
serialised = result
|
||||
for pos in range(0, len(serialized), 80 - prefix_length):
|
||||
result += prefix + serialized[pos : pos + 80 - prefix_length] + "\n"
|
||||
serialized = result
|
||||
|
||||
return serialised
|
||||
return serialized
|
|
@ -137,8 +137,6 @@ class LayerView(View):
|
|||
|
||||
self.currentLayerNumChanged.emit()
|
||||
|
||||
currentLayerNumChanged = Signal()
|
||||
|
||||
def calculateMaxLayers(self):
|
||||
scene = self.getController().getScene()
|
||||
renderer = self.getRenderer() # TODO: @UnusedVariable
|
||||
|
|
|
@ -54,9 +54,13 @@ Item
|
|||
horizontalAlignment: TextInput.AlignRight;
|
||||
onEditingFinished:
|
||||
{
|
||||
// Ensure that the cursor is at the first position. On some systems the text isn't fully visible
|
||||
// Seems to have to do something with different dpi densities that QML doesn't quite handle.
|
||||
// Another option would be to increase the size even further, but that gives pretty ugly results.
|
||||
cursorPosition = 0;
|
||||
if(valueLabel.text != '')
|
||||
{
|
||||
slider.value = valueLabel.text - 1
|
||||
slider.value = valueLabel.text - 1;
|
||||
}
|
||||
}
|
||||
validator: IntValidator { bottom: 1; top: slider.maximumValue + 1; }
|
||||
|
@ -66,10 +70,6 @@ Item
|
|||
anchors.verticalCenter: parent.verticalCenter;
|
||||
|
||||
width: Math.max(UM.Theme.getSize("line").width * maxValue.length + 2, 20);
|
||||
// Ensure that the cursor is at the first position. On some systems the text isnt fully visible
|
||||
// Seems to have to do something with different dpi densities that QML doesn't quite handle.
|
||||
// Another option would be to increase the size even further, but that gives pretty ugly results.
|
||||
onTextChanged: cursorPosition = 0
|
||||
style: TextFieldStyle
|
||||
{
|
||||
textColor: UM.Theme.getColor("setting_control_text");
|
||||
|
|
|
@ -5,7 +5,7 @@ from UM.Logger import Logger
|
|||
|
||||
import UM.Settings.Models
|
||||
|
||||
from cura.SettingOverrideDecorator import SettingOverrideDecorator
|
||||
from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator
|
||||
|
||||
## The per object setting visibility handler ensures that only setting defintions that have a matching instance Container
|
||||
# are returned as visible.
|
||||
|
|
|
@ -5,7 +5,7 @@ from UM.Tool import Tool
|
|||
from UM.Scene.Selection import Selection
|
||||
from UM.Application import Application
|
||||
from UM.Preferences import Preferences
|
||||
from cura.SettingOverrideDecorator import SettingOverrideDecorator
|
||||
from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator
|
||||
|
||||
## This tool allows the user to add & change settings per node in the scene.
|
||||
# The settings per object are kept in a ContainerStack, which is linked to a node by decorator.
|
||||
|
|
|
@ -9,6 +9,7 @@ from UM.Scene.SceneNode import SceneNode
|
|||
from UM.Message import Message
|
||||
from UM.i18n import i18nCatalog
|
||||
from UM.Logger import Logger
|
||||
from UM.Platform import Platform
|
||||
|
||||
import collections
|
||||
import json
|
||||
|
@ -18,6 +19,7 @@ import platform
|
|||
import math
|
||||
import urllib.request
|
||||
import urllib.parse
|
||||
import ssl
|
||||
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
|
@ -45,6 +47,7 @@ class SliceInfo(Extension):
|
|||
Preferences.getInstance().setValue("info/asked_send_slice_info", True)
|
||||
|
||||
def _onWriteStarted(self, output_device):
|
||||
try:
|
||||
if not Preferences.getInstance().getValue("info/send_slice_info"):
|
||||
Logger.log("d", "'info/send_slice_info' is turned off.")
|
||||
return # Do nothing, user does not want to send data
|
||||
|
@ -54,7 +57,9 @@ class SliceInfo(Extension):
|
|||
# Get total material used (in mm^3)
|
||||
print_information = Application.getInstance().getPrintInformation()
|
||||
material_radius = 0.5 * global_container_stack.getProperty("material_diameter", "value")
|
||||
material_used = math.pi * material_radius * material_radius * print_information.materialAmount #Volume of material used
|
||||
|
||||
# TODO: Send material per extruder instead of mashing it on a pile
|
||||
material_used = math.pi * material_radius * material_radius * sum(print_information.materialAmounts) #Volume of all materials used
|
||||
|
||||
# Get model information (bounding boxes, hashes and transformation matrix)
|
||||
models_info = []
|
||||
|
@ -108,9 +113,18 @@ class SliceInfo(Extension):
|
|||
binary_data = submitted_data.encode("utf-8")
|
||||
|
||||
# Submit data
|
||||
kwoptions = {"data" : binary_data,
|
||||
"timeout" : 1
|
||||
}
|
||||
if Platform.isOSX():
|
||||
kwoptions["context"] = ssl._create_unverified_context()
|
||||
try:
|
||||
f = urllib.request.urlopen(self.info_url, data = binary_data, timeout = 1)
|
||||
f = urllib.request.urlopen(self.info_url, **kwoptions)
|
||||
Logger.log("i", "Sent anonymous slice info to %s", self.info_url)
|
||||
f.close()
|
||||
except Exception as e:
|
||||
Logger.logException("e", e)
|
||||
Logger.logException("e", "An exception occurred while trying to send slice information")
|
||||
except:
|
||||
# We really can't afford to have a mistake here, as this would break the sending of g-code to a device
|
||||
# (Either saving or directly to a printer). The functionality of the slice data is not *that* important.
|
||||
pass
|
|
@ -10,7 +10,7 @@ from UM.View.Renderer import Renderer
|
|||
|
||||
from UM.View.GL.OpenGL import OpenGL
|
||||
|
||||
from cura.ExtrudersModel import ExtrudersModel
|
||||
import cura.Settings
|
||||
|
||||
import math
|
||||
|
||||
|
@ -24,7 +24,7 @@ class SolidView(View):
|
|||
self._enabled_shader = None
|
||||
self._disabled_shader = None
|
||||
|
||||
self._extruders_model = ExtrudersModel()
|
||||
self._extruders_model = cura.Settings.ExtrudersModel()
|
||||
|
||||
def beginRendering(self):
|
||||
scene = self.getController().getScene()
|
||||
|
|
|
@ -1,72 +0,0 @@
|
|||
// Copyright (c) 2015 Ultimaker B.V.
|
||||
// Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.1
|
||||
import QtQuick.Controls 1.1
|
||||
import QtQuick.Layouts 1.1
|
||||
import QtQuick.Window 2.1
|
||||
|
||||
import UM 1.1 as UM
|
||||
|
||||
UM.Dialog
|
||||
{
|
||||
width: 500 * Screen.devicePixelRatio;
|
||||
height: 100 * Screen.devicePixelRatio;
|
||||
modality: Qt.NonModal
|
||||
|
||||
title: catalog.i18nc("@title:window", "Print via USB")
|
||||
|
||||
Column
|
||||
{
|
||||
anchors.fill: parent;
|
||||
Row
|
||||
{
|
||||
spacing: UM.Theme.getSize("default_margin").width;
|
||||
Label
|
||||
{
|
||||
//: USB Printing dialog label, %1 is head temperature
|
||||
text: catalog.i18nc("@label","Extruder Temperature %1").arg(manager.hotendTemperatures[0])
|
||||
}
|
||||
Label
|
||||
{
|
||||
//: USB Printing dialog label, %1 is bed temperature
|
||||
text: catalog.i18nc("@label","Bed Temperature %1").arg(manager.bedTemperature)
|
||||
}
|
||||
Label
|
||||
{
|
||||
text: "" + manager.error
|
||||
}
|
||||
|
||||
UM.I18nCatalog{id: catalog; name:"cura"}
|
||||
|
||||
}
|
||||
|
||||
ProgressBar
|
||||
{
|
||||
id: prog;
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
|
||||
minimumValue: 0;
|
||||
maximumValue: 100;
|
||||
value: manager.progress
|
||||
}
|
||||
}
|
||||
|
||||
rightButtons: [
|
||||
Button
|
||||
{
|
||||
//: USB Printing dialog start print button
|
||||
text: catalog.i18nc("@action:button","Print");
|
||||
onClicked: { manager.startPrint() }
|
||||
enabled: manager.progress == 0 ? true : false
|
||||
},
|
||||
Button
|
||||
{
|
||||
//: USB Printing dialog cancel print button
|
||||
text: catalog.i18nc("@action:button","Cancel");
|
||||
onClicked: { manager.cancelPrint() }
|
||||
enabled: manager.progress == 0 ? false: true
|
||||
}
|
||||
]
|
||||
}
|
|
@ -26,8 +26,8 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
|||
def __init__(self, serial_port):
|
||||
super().__init__(serial_port)
|
||||
self.setName(catalog.i18nc("@item:inmenu", "USB printing"))
|
||||
self.setShortDescription(catalog.i18nc("@action:button", "Print with USB"))
|
||||
self.setDescription(catalog.i18nc("@info:tooltip", "Print with USB"))
|
||||
self.setShortDescription(catalog.i18nc("@action:button", "Print via USB"))
|
||||
self.setDescription(catalog.i18nc("@info:tooltip", "Print via USB"))
|
||||
self.setIconName("print")
|
||||
|
||||
self._serial = None
|
||||
|
@ -37,9 +37,8 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
|||
self._connect_thread = threading.Thread(target = self._connect)
|
||||
self._connect_thread.daemon = True
|
||||
|
||||
self._end_stop_thread = threading.Thread(target = self._pollEndStop)
|
||||
self._end_stop_thread.daemon = True
|
||||
self._poll_endstop = -1
|
||||
self._end_stop_thread = None
|
||||
self._poll_endstop = False
|
||||
|
||||
# The baud checking is done by sending a number of m105 commands to the printer and waiting for a readable
|
||||
# response. If the baudrate is correct, this should make sense, else we get giberish.
|
||||
|
@ -58,6 +57,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
|||
self._command_queue = queue.Queue()
|
||||
|
||||
self._is_printing = False
|
||||
self._is_paused = False
|
||||
|
||||
## Set when print is started in order to check running time.
|
||||
self._print_start_time = None
|
||||
|
@ -82,11 +82,13 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
|||
# This index is the extruder we requested data from the last time.
|
||||
self._temperature_requested_extruder_index = 0
|
||||
|
||||
self._current_z = 0
|
||||
|
||||
self._updating_firmware = False
|
||||
|
||||
self._firmware_file_name = None
|
||||
|
||||
self._control_view = None
|
||||
self._error_message = None
|
||||
|
||||
onError = pyqtSignal()
|
||||
|
||||
|
@ -120,10 +122,10 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
|||
def _homeBed(self):
|
||||
self._sendCommand("G28 Z")
|
||||
|
||||
@pyqtSlot()
|
||||
def startPrint(self):
|
||||
self.writeStarted.emit(self)
|
||||
gcode_list = getattr( Application.getInstance().getController().getScene(), "gcode_list")
|
||||
self._updateJobState("printing")
|
||||
self.printGCode(gcode_list)
|
||||
|
||||
def _moveHead(self, x, y, z, speed):
|
||||
|
@ -135,6 +137,8 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
|||
# \param gcode_list List with gcode (strings).
|
||||
def printGCode(self, gcode_list):
|
||||
if self._progress or self._connection_state != ConnectionState.connected:
|
||||
self._error_message = Message(i18n_catalog.i18nc("@info:status", "Printer is busy or not connected. Unable to start a new job."))
|
||||
self._error_message.show()
|
||||
Logger.log("d", "Printer is busy or not connected, aborting print")
|
||||
self.writeError.emit(self)
|
||||
return
|
||||
|
@ -216,13 +220,17 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
|||
|
||||
@pyqtSlot()
|
||||
def startPollEndstop(self):
|
||||
if self._poll_endstop == -1:
|
||||
if not self._poll_endstop:
|
||||
self._poll_endstop = True
|
||||
if self._end_stop_thread is None:
|
||||
self._end_stop_thread = threading.Thread(target=self._pollEndStop)
|
||||
self._end_stop_thread.daemon = True
|
||||
self._end_stop_thread.start()
|
||||
|
||||
@pyqtSlot()
|
||||
def stopPollEndstop(self):
|
||||
self._poll_endstop = False
|
||||
self._end_stop_thread = None
|
||||
|
||||
def _pollEndStop(self):
|
||||
while self._connection_state == ConnectionState.connected and self._poll_endstop:
|
||||
|
@ -344,22 +352,6 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
|||
self._setErrorState("Unexpected error while writing serial port %s " % e)
|
||||
self.close()
|
||||
|
||||
def createControlInterface(self):
|
||||
if self._control_view is None:
|
||||
Logger.log("d", "Creating control interface for printer connection")
|
||||
path = QUrl.fromLocalFile(os.path.join(PluginRegistry.getInstance().getPluginPath("USBPrinting"), "ControlWindow.qml"))
|
||||
component = QQmlComponent(Application.getInstance()._engine, path)
|
||||
self._control_context = QQmlContext(Application.getInstance()._engine.rootContext())
|
||||
self._control_context.setContextProperty("manager", self)
|
||||
self._control_view = component.create(self._control_context)
|
||||
|
||||
## Show control interface.
|
||||
# This will create the view if its not already created.
|
||||
def showControlInterface(self):
|
||||
if self._control_view is None:
|
||||
self.createControlInterface()
|
||||
self._control_view.show()
|
||||
|
||||
## Send a command to printer.
|
||||
# \param cmd string with g-code
|
||||
def sendCommand(self, cmd):
|
||||
|
@ -371,11 +363,13 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
|||
## Set the error state with a message.
|
||||
# \param error String with the error message.
|
||||
def _setErrorState(self, error):
|
||||
self._updateJobState("error")
|
||||
self._error_state = error
|
||||
self.onError.emit()
|
||||
|
||||
def requestWrite(self, node, file_name = None, filter_by_machine = False):
|
||||
self.showControlInterface()
|
||||
Application.getInstance().showPrintMonitor.emit(True)
|
||||
self.startPrint()
|
||||
|
||||
def _setEndstopState(self, endstop_key, value):
|
||||
if endstop_key == b"x_min":
|
||||
|
@ -445,6 +439,8 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
|||
ok_timeout = time.time() + 5
|
||||
if not self._command_queue.empty():
|
||||
self._sendCommand(self._command_queue.get())
|
||||
elif self._is_paused:
|
||||
line = b"" # Force getting temperature as keep alive
|
||||
else:
|
||||
self._sendNextGcodeLine()
|
||||
elif b"resend" in line.lower() or b"rs" in line: # Because a resend can be asked with "resend" and "rs"
|
||||
|
@ -454,13 +450,14 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
|||
if b"rs" in line:
|
||||
self._gcode_position = int(line.split()[1])
|
||||
|
||||
else: # Request the temperature on comm timeout (every 2 seconds) when we are not printing.)
|
||||
# Request the temperature on comm timeout (every 2 seconds) when we are not printing.)
|
||||
if line == b"":
|
||||
if self._num_extruders > 0:
|
||||
self._temperature_requested_extruder_index = (self._temperature_requested_extruder_index + 1) % self._num_extruders
|
||||
self.sendCommand("M105 T%d" % self._temperature_requested_extruder_index)
|
||||
else:
|
||||
self.sendCommand("M105")
|
||||
|
||||
Logger.log("i", "Printer connection listen thread stopped for %s" % self._serial_port)
|
||||
|
||||
## Send next Gcode in the gcode list
|
||||
|
@ -491,6 +488,18 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
|||
self.setProgress((self._gcode_position / len(self._gcode)) * 100)
|
||||
self.progressChanged.emit()
|
||||
|
||||
## Set the state of the print.
|
||||
# Sent from the print monitor
|
||||
def _setJobState(self, job_state):
|
||||
if job_state == "pause":
|
||||
self._is_paused = True
|
||||
self._updateJobState("paused")
|
||||
elif job_state == "print":
|
||||
self._is_paused = False
|
||||
self._updateJobState("printing")
|
||||
elif job_state == "abort":
|
||||
self.cancelPrint()
|
||||
|
||||
## Set the progress of the print.
|
||||
# It will be normalized (based on max_progress) to range 0 - 100
|
||||
def setProgress(self, progress, max_progress = 100):
|
||||
|
@ -498,16 +507,20 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
|||
self.progressChanged.emit()
|
||||
|
||||
## Cancel the current print. Printer connection wil continue to listen.
|
||||
@pyqtSlot()
|
||||
def cancelPrint(self):
|
||||
self._gcode_position = 0
|
||||
self.setProgress(0)
|
||||
self._gcode = []
|
||||
|
||||
# Turn of temperatures
|
||||
# Turn off temperatures, fan and steppers
|
||||
self._sendCommand("M140 S0")
|
||||
self._sendCommand("M104 S0")
|
||||
self._sendCommand("M107")
|
||||
self._sendCommand("M84")
|
||||
self._is_printing = False
|
||||
self._is_paused = False
|
||||
self._updateJobState("ready")
|
||||
Application.getInstance().showPrintMonitor.emit(False)
|
||||
|
||||
## Check if the process did not encounter an error yet.
|
||||
def hasError(self):
|
||||
|
|
|
@ -41,10 +41,6 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin, Extension):
|
|||
self._check_updates = True
|
||||
self._firmware_view = None
|
||||
|
||||
## Add menu item to top menu of the application.
|
||||
self.setMenuName(i18n_catalog.i18nc("@title:menu","Firmware"))
|
||||
self.addMenuItem(i18n_catalog.i18nc("@item:inmenu", "Update Firmware"), self.updateAllFirmware)
|
||||
|
||||
Application.getInstance().applicationShuttingDown.connect(self.stop)
|
||||
self.addUSBOutputDeviceSignal.connect(self.addOutputDevice) #Because the model needs to be created in the same thread as the QMLEngine, we use a signal.
|
||||
|
||||
|
@ -156,7 +152,7 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin, Extension):
|
|||
"ultimaker_original_plus" : "MarlinUltimaker-UMOP-{baudrate}.hex",
|
||||
"ultimaker2" : "MarlinUltimaker2.hex",
|
||||
"ultimaker2_go" : "MarlinUltimaker2go.hex",
|
||||
"ultimaker2plus" : "MarlinUltimaker2plus.hex",
|
||||
"ultimaker2_plus" : "MarlinUltimaker2plus.hex",
|
||||
"ultimaker2_extended" : "MarlinUltimaker2extended.hex",
|
||||
"ultimaker2_extended_plus" : "MarlinUltimaker2extended-plus.hex",
|
||||
}
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
from cura.MachineAction import MachineAction
|
||||
from cura.PrinterOutputDevice import PrinterOutputDevice
|
||||
|
||||
from PyQt5.QtCore import pyqtSlot
|
||||
|
||||
from UM.Application import Application
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
from cura.PrinterOutputDevice import PrinterOutputDevice
|
||||
|
||||
class BedLevelMachineAction(MachineAction):
|
||||
def __init__(self):
|
||||
super().__init__("BedLevel", "Level bed")
|
||||
super().__init__("BedLevel", catalog.i18nc("@action", "Level bed"))
|
||||
self._qml_url = "BedLevelMachineAction.qml"
|
||||
self._bed_level_position = 0
|
||||
|
||||
|
|
|
@ -3,9 +3,14 @@ from cura.PrinterOutputDevice import PrinterOutputDevice
|
|||
from UM.Application import Application
|
||||
from PyQt5.QtCore import pyqtSlot, pyqtSignal, pyqtProperty
|
||||
|
||||
from UM.Logger import Logger
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
|
||||
class UMOCheckupMachineAction(MachineAction):
|
||||
def __init__(self):
|
||||
super().__init__("UMOCheckup", "Checkup")
|
||||
super().__init__("UMOCheckup", catalog.i18nc("@action", "Checkup"))
|
||||
self._qml_url = "UMOCheckupMachineAction.qml"
|
||||
self._hotend_target_temp = 180
|
||||
self._bed_target_temp = 60
|
||||
|
@ -39,7 +44,6 @@ class UMOCheckupMachineAction(MachineAction):
|
|||
if self._output_device is None and self._check_started:
|
||||
self.startCheck()
|
||||
|
||||
|
||||
def _getPrinterOutputDevices(self):
|
||||
return [printer_output_device for printer_output_device in
|
||||
Application.getInstance().getOutputDeviceManager().getOutputDevices() if
|
||||
|
@ -54,11 +58,13 @@ class UMOCheckupMachineAction(MachineAction):
|
|||
self._output_device.endstopStateChanged.disconnect(self._onEndstopStateChanged)
|
||||
try:
|
||||
self._output_device.stopPollEndstop()
|
||||
except AttributeError: # Connection is probably not a USB connection. Something went pretty wrong if this happens.
|
||||
pass
|
||||
except AttributeError as e: # Connection is probably not a USB connection. Something went pretty wrong if this happens.
|
||||
Logger.log("e", "An exception occurred while stopping end stop polling: %s" % str(e))
|
||||
|
||||
self._output_device = None
|
||||
|
||||
self._check_started = False
|
||||
self.checkStartedChanged.emit()
|
||||
|
||||
# Ensure everything is reset (and right signals are emitted again)
|
||||
self._bed_test_completed = False
|
||||
|
@ -73,6 +79,8 @@ class UMOCheckupMachineAction(MachineAction):
|
|||
self._z_min_endstop_test_completed = False
|
||||
self.onZMinEndstopTestCompleted.emit()
|
||||
|
||||
self.heatedBedChanged.emit()
|
||||
|
||||
@pyqtProperty(bool, notify = onBedTestCompleted)
|
||||
def bedTestCompleted(self):
|
||||
return self._bed_test_completed
|
||||
|
@ -133,9 +141,16 @@ class UMOCheckupMachineAction(MachineAction):
|
|||
self._z_min_endstop_test_completed = True
|
||||
self.onZMinEndstopTestCompleted.emit()
|
||||
|
||||
checkStartedChanged = pyqtSignal()
|
||||
|
||||
@pyqtProperty(bool, notify = checkStartedChanged)
|
||||
def checkStarted(self):
|
||||
return self._check_started
|
||||
|
||||
@pyqtSlot()
|
||||
def startCheck(self):
|
||||
self._check_started = True
|
||||
self.checkStartedChanged.emit()
|
||||
output_devices = self._getPrinterOutputDevices()
|
||||
if output_devices:
|
||||
self._output_device = output_devices[0]
|
||||
|
@ -146,8 +161,18 @@ class UMOCheckupMachineAction(MachineAction):
|
|||
self._output_device.bedTemperatureChanged.connect(self._onBedTemperatureChanged)
|
||||
self._output_device.hotendTemperaturesChanged.connect(self._onHotendTemperatureChanged)
|
||||
self._output_device.endstopStateChanged.connect(self._onEndstopStateChanged)
|
||||
except AttributeError: # Connection is probably not a USB connection. Something went pretty wrong if this happens.
|
||||
pass
|
||||
except AttributeError as e: # Connection is probably not a USB connection. Something went pretty wrong if this happens.
|
||||
Logger.log("e", "An exception occurred while starting end stop polling: %s" % str(e))
|
||||
|
||||
@pyqtSlot()
|
||||
def cooldownHotend(self):
|
||||
if self._output_device is not None:
|
||||
self._output_device.setTargetHotendTemperature(0, 0)
|
||||
|
||||
@pyqtSlot()
|
||||
def cooldownBed(self):
|
||||
if self._output_device is not None:
|
||||
self._output_device.setTargetBedTemperature(0)
|
||||
|
||||
@pyqtSlot()
|
||||
def heatupHotend(self):
|
||||
|
@ -158,3 +183,10 @@ class UMOCheckupMachineAction(MachineAction):
|
|||
def heatupBed(self):
|
||||
if self._output_device is not None:
|
||||
self._output_device.setTargetBedTemperature(self._bed_target_temp)
|
||||
|
||||
heatedBedChanged = pyqtSignal()
|
||||
|
||||
@pyqtProperty(bool, notify = heatedBedChanged)
|
||||
def hasHeatedBed(self):
|
||||
global_container_stack = Application.getInstance().getGlobalContainerStack()
|
||||
return global_container_stack.getProperty("machine_heated_bed", "value")
|
|
@ -15,6 +15,10 @@ Cura.MachineAction
|
|||
anchors.fill: parent;
|
||||
property int leftRow: checkupMachineAction.width * 0.40
|
||||
property int rightRow: checkupMachineAction.width * 0.60
|
||||
property bool heatupHotendStarted: false
|
||||
property bool heatupBedStarted: false
|
||||
property bool usbConnected: Cura.USBPrinterManager.connectedPrinterList.rowCount() > 0
|
||||
|
||||
UM.I18nCatalog { id: catalog; name:"cura"}
|
||||
Label
|
||||
{
|
||||
|
@ -32,7 +36,7 @@ Cura.MachineAction
|
|||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@label","It's a good idea to do a few sanity checks on your Ultimaker. You can skip this step if you know your machine is functional");
|
||||
text: catalog.i18nc("@label", "It's a good idea to do a few sanity checks on your Ultimaker. You can skip this step if you know your machine is functional");
|
||||
}
|
||||
|
||||
Item
|
||||
|
@ -51,8 +55,8 @@ Cura.MachineAction
|
|||
text: catalog.i18nc("@action:button","Start Printer Check");
|
||||
onClicked:
|
||||
{
|
||||
checkupContent.visible = true
|
||||
startCheckButton.enabled = false
|
||||
checkupMachineAction.heatupHotendStarted = false
|
||||
checkupMachineAction.heatupBedStarted = false
|
||||
manager.startCheck()
|
||||
}
|
||||
}
|
||||
|
@ -74,7 +78,7 @@ Cura.MachineAction
|
|||
id: checkupContent
|
||||
anchors.top: startStopButtons.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
visible: false
|
||||
visible: manager.checkStarted
|
||||
width: parent.width
|
||||
height: 250
|
||||
//////////////////////////////////////////////////////////
|
||||
|
@ -94,7 +98,7 @@ Cura.MachineAction
|
|||
anchors.left: connectionLabel.right
|
||||
anchors.top: parent.top
|
||||
wrapMode: Text.WordWrap
|
||||
text: Cura.USBPrinterManager.connectedPrinterList.rowCount() > 0 || base.addOriginalProgress.checkUp[0] ? catalog.i18nc("@info:status","Done"):catalog.i18nc("@info:status","Incomplete")
|
||||
text: checkupMachineAction.usbConnected ? catalog.i18nc("@info:status","Connected"): catalog.i18nc("@info:status","Not connected")
|
||||
}
|
||||
//////////////////////////////////////////////////////////
|
||||
Label
|
||||
|
@ -105,6 +109,7 @@ Cura.MachineAction
|
|||
anchors.top: connectionLabel.bottom
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@label","Min endstop X: ")
|
||||
visible: checkupMachineAction.usbConnected
|
||||
}
|
||||
Label
|
||||
{
|
||||
|
@ -114,6 +119,7 @@ Cura.MachineAction
|
|||
anchors.top: connectionLabel.bottom
|
||||
wrapMode: Text.WordWrap
|
||||
text: manager.xMinEndstopTestCompleted ? catalog.i18nc("@info:status","Works") : catalog.i18nc("@info:status","Not checked")
|
||||
visible: checkupMachineAction.usbConnected
|
||||
}
|
||||
//////////////////////////////////////////////////////////////
|
||||
Label
|
||||
|
@ -124,6 +130,7 @@ Cura.MachineAction
|
|||
anchors.top: endstopXLabel.bottom
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@label","Min endstop Y: ")
|
||||
visible: checkupMachineAction.usbConnected
|
||||
}
|
||||
Label
|
||||
{
|
||||
|
@ -133,6 +140,7 @@ Cura.MachineAction
|
|||
anchors.top: endstopXLabel.bottom
|
||||
wrapMode: Text.WordWrap
|
||||
text: manager.yMinEndstopTestCompleted ? catalog.i18nc("@info:status","Works") : catalog.i18nc("@info:status","Not checked")
|
||||
visible: checkupMachineAction.usbConnected
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
Label
|
||||
|
@ -143,6 +151,7 @@ Cura.MachineAction
|
|||
anchors.top: endstopYLabel.bottom
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@label","Min endstop Z: ")
|
||||
visible: checkupMachineAction.usbConnected
|
||||
}
|
||||
Label
|
||||
{
|
||||
|
@ -152,16 +161,19 @@ Cura.MachineAction
|
|||
anchors.top: endstopYLabel.bottom
|
||||
wrapMode: Text.WordWrap
|
||||
text: manager.zMinEndstopTestCompleted ? catalog.i18nc("@info:status","Works") : catalog.i18nc("@info:status","Not checked")
|
||||
visible: checkupMachineAction.usbConnected
|
||||
}
|
||||
////////////////////////////////////////////////////////////
|
||||
Label
|
||||
{
|
||||
id: nozzleTempLabel
|
||||
width: checkupMachineAction.leftRow
|
||||
height: nozzleTempButton.height
|
||||
anchors.left: parent.left
|
||||
anchors.top: endstopZLabel.bottom
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@label","Nozzle temperature check: ")
|
||||
visible: checkupMachineAction.usbConnected
|
||||
}
|
||||
Label
|
||||
{
|
||||
|
@ -171,25 +183,32 @@ Cura.MachineAction
|
|||
anchors.left: nozzleTempLabel.right
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@info:status","Not checked")
|
||||
visible: checkupMachineAction.usbConnected
|
||||
}
|
||||
Item
|
||||
{
|
||||
id: nozzleTempButton
|
||||
width: checkupMachineAction.rightRow * 0.3
|
||||
height: nozzleTemp.height
|
||||
height: childrenRect.height
|
||||
anchors.top: nozzleTempLabel.top
|
||||
anchors.left: bedTempStatus.right
|
||||
anchors.leftMargin: UM.Theme.getSize("default_margin").width/2
|
||||
visible: checkupMachineAction.usbConnected
|
||||
Button
|
||||
{
|
||||
height: nozzleTemp.height - 2
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: catalog.i18nc("@action:button","Start Heating")
|
||||
text: checkupMachineAction.heatupHotendStarted ? catalog.i18nc("@action:button","Stop Heating") : catalog.i18nc("@action:button","Start Heating")
|
||||
//
|
||||
onClicked:
|
||||
{
|
||||
if (checkupMachineAction.heatupHotendStarted)
|
||||
{
|
||||
manager.cooldownHotend()
|
||||
checkupMachineAction.heatupHotendStarted = false
|
||||
} else
|
||||
{
|
||||
manager.heatupHotend()
|
||||
nozzleTempStatus.text = catalog.i18nc("@info:progress","Checking")
|
||||
checkupMachineAction.heatupHotendStarted = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -203,16 +222,19 @@ Cura.MachineAction
|
|||
wrapMode: Text.WordWrap
|
||||
text: manager.hotendTemperature + "°C"
|
||||
font.bold: true
|
||||
visible: checkupMachineAction.usbConnected
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
Label
|
||||
{
|
||||
id: bedTempLabel
|
||||
width: checkupMachineAction.leftRow
|
||||
height: bedTempButton.height
|
||||
anchors.left: parent.left
|
||||
anchors.top: nozzleTempLabel.bottom
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@label","bed temperature check:")
|
||||
text: catalog.i18nc("@label","Bed temperature check:")
|
||||
visible: checkupMachineAction.usbConnected && manager.hasHeatedBed
|
||||
}
|
||||
|
||||
Label
|
||||
|
@ -223,24 +245,31 @@ Cura.MachineAction
|
|||
anchors.left: bedTempLabel.right
|
||||
wrapMode: Text.WordWrap
|
||||
text: manager.bedTestCompleted ? catalog.i18nc("@info:status","Not checked"): catalog.i18nc("@info:status","Checked")
|
||||
visible: checkupMachineAction.usbConnected && manager.hasHeatedBed
|
||||
}
|
||||
Item
|
||||
{
|
||||
id: bedTempButton
|
||||
width: checkupMachineAction.rightRow * 0.3
|
||||
height: bedTemp.height
|
||||
height: childrenRect.height
|
||||
anchors.top: bedTempLabel.top
|
||||
anchors.left: bedTempStatus.right
|
||||
anchors.leftMargin: UM.Theme.getSize("default_margin").width/2
|
||||
visible: checkupMachineAction.usbConnected && manager.hasHeatedBed
|
||||
Button
|
||||
{
|
||||
height: bedTemp.height - 2
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: catalog.i18nc("@action:button","Start Heating")
|
||||
text: checkupMachineAction.heatupBedStarted ?catalog.i18nc("@action:button","Stop Heating") : catalog.i18nc("@action:button","Start Heating")
|
||||
onClicked:
|
||||
{
|
||||
if (checkupMachineAction.heatupBedStarted)
|
||||
{
|
||||
manager.cooldownBed()
|
||||
checkupMachineAction.heatupBedStarted = false
|
||||
} else
|
||||
{
|
||||
manager.heatupBed()
|
||||
checkupMachineAction.heatupBedStarted = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -254,6 +283,7 @@ Cura.MachineAction
|
|||
wrapMode: Text.WordWrap
|
||||
text: manager.bedTemperature + "°C"
|
||||
font.bold: true
|
||||
visible: checkupMachineAction.usbConnected && manager.hasHeatedBed
|
||||
}
|
||||
Label
|
||||
{
|
||||
|
|
47
plugins/UltimakerMachineActions/UMOUpgradeSelection.py
Normal file
47
plugins/UltimakerMachineActions/UMOUpgradeSelection.py
Normal file
|
@ -0,0 +1,47 @@
|
|||
from cura.MachineAction import MachineAction
|
||||
from PyQt5.QtCore import pyqtSlot, pyqtSignal, pyqtProperty
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
from UM.Application import Application
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
import UM.Settings.InstanceContainer
|
||||
|
||||
class UMOUpgradeSelection(MachineAction):
|
||||
def __init__(self):
|
||||
super().__init__("UMOUpgradeSelection", catalog.i18nc("@action", "Select upgrades"))
|
||||
self._qml_url = "UMOUpgradeSelectionMachineAction.qml"
|
||||
|
||||
heatedBedChanged = pyqtSignal()
|
||||
|
||||
@pyqtProperty(bool, notify = heatedBedChanged)
|
||||
def hasHeatedBed(self):
|
||||
global_container_stack = Application.getInstance().getGlobalContainerStack()
|
||||
return global_container_stack.getProperty("machine_heated_bed", "value")
|
||||
|
||||
@pyqtSlot()
|
||||
def addHeatedBed(self):
|
||||
global_container_stack = Application.getInstance().getGlobalContainerStack()
|
||||
if global_container_stack:
|
||||
variant = global_container_stack.findContainer({"type": "variant"})
|
||||
if variant:
|
||||
if variant.getId() == "empty_variant":
|
||||
variant_index = global_container_stack.getContainerIndex(variant)
|
||||
stack_name = global_container_stack.getName()
|
||||
new_variant = UM.Settings.InstanceContainer(stack_name + "_variant")
|
||||
new_variant.addMetaDataEntry("type", "variant")
|
||||
new_variant.setDefinition(global_container_stack.getBottom())
|
||||
UM.Settings.ContainerRegistry.getInstance().addContainer(new_variant)
|
||||
global_container_stack.replaceContainer(variant_index, new_variant)
|
||||
variant = new_variant
|
||||
variant.setProperty("machine_heated_bed", "value", True)
|
||||
self.heatedBedChanged.emit()
|
||||
|
||||
@pyqtSlot()
|
||||
def removeHeatedBed(self):
|
||||
global_container_stack = Application.getInstance().getGlobalContainerStack()
|
||||
if global_container_stack:
|
||||
variant = global_container_stack.findContainer({"type": "variant"})
|
||||
if variant:
|
||||
variant.setProperty("machine_heated_bed", "value", False)
|
||||
self.heatedBedChanged.emit()
|
|
@ -0,0 +1,52 @@
|
|||
// Copyright (c) 2016 Ultimaker B.V.
|
||||
// Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.2
|
||||
import QtQuick.Controls 1.1
|
||||
import QtQuick.Layouts 1.1
|
||||
import QtQuick.Window 2.1
|
||||
|
||||
import UM 1.2 as UM
|
||||
import Cura 1.0 as Cura
|
||||
|
||||
|
||||
Cura.MachineAction
|
||||
{
|
||||
anchors.fill: parent;
|
||||
Item
|
||||
{
|
||||
id: upgradeSelectionMachineAction
|
||||
anchors.fill: parent
|
||||
|
||||
Label
|
||||
{
|
||||
id: pageTitle
|
||||
width: parent.width
|
||||
text: catalog.i18nc("@title", "Check Printer")
|
||||
wrapMode: Text.WordWrap
|
||||
font.pointSize: 18;
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
id: pageDescription
|
||||
anchors.top: pageTitle.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@label","Please select any upgrades made to this Ultimaker Original");
|
||||
}
|
||||
|
||||
CheckBox
|
||||
{
|
||||
anchors.top: pageDescription.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
|
||||
text: catalog.i18nc("@label", "Self-built heated bed")
|
||||
checked: manager.hasHeatedBed
|
||||
onClicked: manager.hasHeatedBed ? manager.removeHeatedBed() : manager.addHeatedBed()
|
||||
}
|
||||
|
||||
UM.I18nCatalog { id: catalog; name: "cura"; }
|
||||
}
|
||||
}
|
|
@ -1,6 +1,9 @@
|
|||
from cura.MachineAction import MachineAction
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
|
||||
class UpgradeFirmwareMachineAction(MachineAction):
|
||||
def __init__(self):
|
||||
super().__init__("UpgradeFirmware", "Upgrade Firmware")
|
||||
super().__init__("UpgradeFirmware", catalog.i18nc("@action", "Upgrade Firmware"))
|
||||
self._qml_url = "UpgradeFirmwareMachineAction.qml"
|
|
@ -4,6 +4,7 @@
|
|||
from . import BedLevelMachineAction
|
||||
from . import UpgradeFirmwareMachineAction
|
||||
from . import UMOCheckupMachineAction
|
||||
from . import UMOUpgradeSelection
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
@ -20,4 +21,4 @@ def getMetaData():
|
|||
}
|
||||
|
||||
def register(app):
|
||||
return { "machine_action": [BedLevelMachineAction.BedLevelMachineAction(), UpgradeFirmwareMachineAction.UpgradeFirmwareMachineAction(), UMOCheckupMachineAction.UMOCheckupMachineAction()]}
|
||||
return { "machine_action": [BedLevelMachineAction.BedLevelMachineAction(), UpgradeFirmwareMachineAction.UpgradeFirmwareMachineAction(), UMOCheckupMachineAction.UMOCheckupMachineAction(), UMOUpgradeSelection.UMOUpgradeSelection()]}
|
||||
|
|
100
plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py
Normal file
100
plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py
Normal file
|
@ -0,0 +1,100 @@
|
|||
# Copyright (c) 2016 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
import UM.VersionUpgrade #To indicate that a file is of incorrect format.
|
||||
|
||||
import configparser #To read config files.
|
||||
import io #To write config files to strings as if they were files.
|
||||
|
||||
## Creates a new machine instance instance by parsing a serialised machine
|
||||
# instance in version 1 of the file format.
|
||||
#
|
||||
# \param serialised The serialised form of a machine instance in version 1.
|
||||
# \param filename The supposed file name of this machine instance, without
|
||||
# extension.
|
||||
# \return A machine instance instance, or None if the file format is
|
||||
# incorrect.
|
||||
def importFrom(serialised, filename):
|
||||
try:
|
||||
return MachineInstance(serialised, filename)
|
||||
except (configparser.Error, UM.VersionUpgrade.FormatException, UM.VersionUpgrade.InvalidVersionException):
|
||||
return None
|
||||
|
||||
## A representation of a machine instance used as intermediary form for
|
||||
# conversion from one format to the other.
|
||||
class MachineInstance:
|
||||
## Reads version 1 of the file format, storing it in memory.
|
||||
#
|
||||
# \param serialised A string with the contents of a machine instance file,
|
||||
# without extension.
|
||||
# \param filename The supposed file name of this machine instance.
|
||||
def __init__(self, serialised, filename):
|
||||
self._filename = filename
|
||||
|
||||
config = configparser.ConfigParser(interpolation = None)
|
||||
config.read_string(serialised) # Read the input string as config file.
|
||||
|
||||
# Checking file correctness.
|
||||
if not config.has_section("general"):
|
||||
raise UM.VersionUpgrade.FormatException("No \"general\" section.")
|
||||
if not config.has_option("general", "version"):
|
||||
raise UM.VersionUpgrade.FormatException("No \"version\" in \"general\" section.")
|
||||
if not config.has_option("general", "name"):
|
||||
raise UM.VersionUpgrade.FormatException("No \"name\" in \"general\" section.")
|
||||
if not config.has_option("general", "type"):
|
||||
raise UM.VersionUpgrade.FormatException("No \"type\" in \"general\" section.")
|
||||
if int(config.get("general", "version")) != 1: # Explicitly hard-code version 1, since if this number changes the programmer MUST change this entire function.
|
||||
raise UM.VersionUpgrade.InvalidVersionException("The version of this machine instance is wrong. It must be 1.")
|
||||
|
||||
self._type_name = config.get("general", "type")
|
||||
self._variant_name = config.get("general", "variant", fallback = "empty")
|
||||
self._name = config.get("general", "name", fallback = "")
|
||||
self._key = config.get("general", "key", fallback = None)
|
||||
self._active_profile_name = config.get("general", "active_profile", fallback = "empty")
|
||||
self._active_material_name = config.get("general", "material", fallback = "empty")
|
||||
|
||||
self._machine_setting_overrides = {}
|
||||
for key, value in config["machine_settings"].items():
|
||||
self._machine_setting_overrides[key] = value
|
||||
|
||||
## Serialises this machine instance as file format version 2.
|
||||
#
|
||||
# This is where the actual translation happens in this case.
|
||||
#
|
||||
# \return A tuple containing the new filename and a serialised form of
|
||||
# this machine instance, serialised in version 2 of the file format.
|
||||
def export(self):
|
||||
config = configparser.ConfigParser(interpolation = None) # Build a config file in the form of version 2.
|
||||
|
||||
config.add_section("general")
|
||||
config.set("general", "name", self._name)
|
||||
config.set("general", "id", self._name)
|
||||
config.set("general", "type", self._type_name)
|
||||
config.set("general", "version", "2") # Hard-code version 2, since if this number changes the programmer MUST change this entire function.
|
||||
|
||||
import VersionUpgrade21to22 # Import here to prevent circular dependencies.
|
||||
type_name = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translatePrinter(self._type_name)
|
||||
active_profile = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateProfile(self._active_profile_name)
|
||||
active_material = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateProfile(self._active_material_name)
|
||||
variant = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateVariant(self._variant_name, type_name)
|
||||
|
||||
containers = [
|
||||
self._name + "_current_settings",
|
||||
active_profile,
|
||||
active_material,
|
||||
variant,
|
||||
type_name
|
||||
]
|
||||
config.set("general", "containers", ",".join(containers))
|
||||
|
||||
config.add_section("metadata")
|
||||
config.set("metadata", "type", "machine")
|
||||
|
||||
VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateSettings(self._machine_setting_overrides)
|
||||
config.add_section("values")
|
||||
for key, value in self._machine_setting_overrides.items():
|
||||
config.set("values", key, str(value))
|
||||
|
||||
output = io.StringIO()
|
||||
config.write(output)
|
||||
return self._filename, output.getvalue()
|
80
plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py
Normal file
80
plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py
Normal file
|
@ -0,0 +1,80 @@
|
|||
# Copyright (c) 2016 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
import configparser #To read config files.
|
||||
import io #To output config files to string.
|
||||
|
||||
import UM.VersionUpgrade #To indicate that a file is of the wrong format.
|
||||
|
||||
## Creates a new preferences instance by parsing a serialised preferences file
|
||||
# in version 1 of the file format.
|
||||
#
|
||||
# \param serialised The serialised form of a preferences file in version 1.
|
||||
# \param filename The supposed filename of the preferences file, without
|
||||
# extension.
|
||||
# \return A representation of those preferences, or None if the file format is
|
||||
# incorrect.
|
||||
def importFrom(serialised, filename):
|
||||
try:
|
||||
return Preferences(serialised, filename)
|
||||
except (configparser.Error, UM.VersionUpgrade.FormatException, UM.VersionUpgrade.InvalidVersionException):
|
||||
return None
|
||||
|
||||
## A representation of preferences files as intermediary form for conversion
|
||||
# from one format to the other.
|
||||
class Preferences:
|
||||
## Reads version 2 of the preferences file format, storing it in memory.
|
||||
#
|
||||
# \param serialised A serialised version 2 preferences file.
|
||||
# \param filename The supposed filename of the preferences file, without
|
||||
# extension.
|
||||
def __init__(self, serialised, filename):
|
||||
self._filename = filename
|
||||
|
||||
self._config = configparser.ConfigParser(interpolation = None)
|
||||
self._config.read_string(serialised)
|
||||
|
||||
#Checking file correctness.
|
||||
if not self._config.has_section("general"):
|
||||
raise UM.VersionUpgrade.FormatException("No \"general\" section.")
|
||||
if not self._config.has_option("general", "version"):
|
||||
raise UM.VersionUpgrade.FormatException("No \"version\" in \"general\" section.")
|
||||
if int(self._config.get("general", "version")) != 2: # Explicitly hard-code version 2, since if this number changes the programmer MUST change this entire function.
|
||||
raise UM.VersionUpgrade.InvalidVersionException("The version of this preferences file is wrong. It must be 2.")
|
||||
if self._config.has_option("general", "name"): #This is probably a machine instance.
|
||||
raise UM.VersionUpgrade.FormatException("There is a \"name\" field in this configuration file. I suspect it is not a preferences file.")
|
||||
|
||||
## Serialises these preferences as a preferences file of version 3.
|
||||
#
|
||||
# This is where the actual translation happens.
|
||||
#
|
||||
# \return A tuple containing the new filename and a serialised version of
|
||||
# a preferences file in version 3.
|
||||
def export(self):
|
||||
#Reset the cura/categories_expanded property since it works differently now.
|
||||
if self._config.has_section("cura") and self._config.has_option("cura", "categories_expanded"):
|
||||
self._config.remove_option("cura", "categories_expanded")
|
||||
|
||||
#Translate the setting names in the visible settings.
|
||||
if self._config.has_section("machines") and self._config.has_option("machines", "setting_visibility"):
|
||||
visible_settings = self._config.get("machines", "setting_visibility")
|
||||
visible_settings = visible_settings.split(",")
|
||||
import VersionUpgrade21to22 #Import here to prevent a circular dependency.
|
||||
visible_settings = [VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateSettingName(setting_name)
|
||||
for setting_name in visible_settings]
|
||||
visible_settings = ",".join(visible_settings)
|
||||
self._config.set("machines", "setting_visibility", value = visible_settings)
|
||||
|
||||
#Translate the active_instance key.
|
||||
if self._config.has_section("machines") and self._config.has_option("machines", "active_instance"):
|
||||
active_machine = self._config.get("machines", "active_instance")
|
||||
self._config.remove_option("machines", "active_instance")
|
||||
self._config.set("cura", "active_machine", active_machine)
|
||||
|
||||
#Update the version number itself.
|
||||
self._config.set("general", "version", value = "3")
|
||||
|
||||
#Output the result as a string.
|
||||
output = io.StringIO()
|
||||
self._config.write(output)
|
||||
return self._filename, output.getvalue()
|
133
plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py
Normal file
133
plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py
Normal file
|
@ -0,0 +1,133 @@
|
|||
# Copyright (c) 2016 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
import configparser #To read config files.
|
||||
import io #To write config files to strings as if they were files.
|
||||
|
||||
import UM.VersionUpgrade
|
||||
|
||||
## Creates a new profile instance by parsing a serialised profile in version 1
|
||||
# of the file format.
|
||||
#
|
||||
# \param serialised The serialised form of a profile in version 1.
|
||||
# \param filename The supposed filename of the profile, without extension.
|
||||
# \return A profile instance, or None if the file format is incorrect.
|
||||
def importFrom(serialised, filename):
|
||||
try:
|
||||
return Profile(serialised, filename)
|
||||
except (configparser.Error, UM.VersionUpgrade.FormatException, UM.VersionUpgrade.InvalidVersionException):
|
||||
return None
|
||||
|
||||
## A representation of a profile used as intermediary form for conversion from
|
||||
# one format to the other.
|
||||
class Profile:
|
||||
## Reads version 1 of the file format, storing it in memory.
|
||||
#
|
||||
# \param serialised A string with the contents of a profile.
|
||||
# \param filename The supposed filename of the profile, without extension.
|
||||
def __init__(self, serialised, filename):
|
||||
self._filename = filename
|
||||
|
||||
parser = configparser.ConfigParser(interpolation = None)
|
||||
parser.read_string(serialised)
|
||||
|
||||
# Check correctness.
|
||||
if not parser.has_section("general"):
|
||||
raise UM.VersionUpgrade.FormatException("No \"general\" section.")
|
||||
if not parser.has_option("general", "version"):
|
||||
raise UM.VersionUpgrade.FormatException("No \"version\" in the \"general\" section.")
|
||||
if int(parser.get("general", "version")) != 1: # Hard-coded profile version here. If this number changes the entire function needs to change.
|
||||
raise UM.VersionUpgrade.InvalidVersionException("The version of this profile is wrong. It must be 1.")
|
||||
|
||||
# Parse the general section.
|
||||
self._name = parser.get("general", "name")
|
||||
self._type = parser.get("general", "type", fallback = None)
|
||||
if "weight" in parser["general"]:
|
||||
self._weight = int(parser.get("general", "weight"))
|
||||
else:
|
||||
self._weight = None
|
||||
self._machine_type_id = parser.get("general", "machine_type", fallback = None)
|
||||
self._machine_variant_name = parser.get("general", "machine_variant", fallback = None)
|
||||
self._machine_instance_name = parser.get("general", "machine_instance", fallback = None)
|
||||
if "material" in parser["general"]:
|
||||
self._material_name = parser.get("general", "material")
|
||||
elif self._type == "material":
|
||||
self._material_name = parser.get("general", "name", fallback = None)
|
||||
else:
|
||||
self._material_name = None
|
||||
|
||||
# Parse the settings.
|
||||
self._settings = {}
|
||||
if parser.has_section("settings"):
|
||||
for key, value in parser["settings"].items():
|
||||
self._settings[key] = value
|
||||
|
||||
# Parse the defaults and the disabled defaults.
|
||||
self._changed_settings_defaults = {}
|
||||
if parser.has_section("defaults"):
|
||||
for key, value in parser["defaults"].items():
|
||||
self._changed_settings_defaults[key] = value
|
||||
self._disabled_settings_defaults = []
|
||||
if parser.has_section("disabled_defaults"):
|
||||
disabled_defaults_string = parser.get("disabled_defaults", "values")
|
||||
self._disabled_settings_defaults = [item for item in disabled_defaults_string.split(",") if item != ""] # Split by comma.
|
||||
|
||||
## Serialises this profile as file format version 2.
|
||||
#
|
||||
# \return A tuple containing the new filename and a serialised form of
|
||||
# this profile, serialised in version 2 of the file format.
|
||||
def export(self):
|
||||
import VersionUpgrade21to22 # Import here to prevent circular dependencies.
|
||||
|
||||
if self._name == "Current settings":
|
||||
self._filename += "_current_settings" #This resolves a duplicate ID arising from how Cura 2.1 stores its current settings.
|
||||
|
||||
config = configparser.ConfigParser(interpolation = None)
|
||||
|
||||
config.add_section("general")
|
||||
config.set("general", "version", "2") #Hard-coded profile version 2.
|
||||
config.set("general", "name", self._name)
|
||||
if self._machine_type_id:
|
||||
translated_machine = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translatePrinter(self._machine_type_id)
|
||||
config.set("general", "definition", translated_machine)
|
||||
else:
|
||||
config.set("general", "definition", "fdmprinter")
|
||||
|
||||
config.add_section("metadata")
|
||||
if self._type:
|
||||
config.set("metadata", "type", self._type)
|
||||
else:
|
||||
config.set("metadata", "type", "quality")
|
||||
if self._weight:
|
||||
config.set("metadata", "weight", self._weight)
|
||||
if self._machine_variant_name:
|
||||
if self._machine_type_id:
|
||||
config.set("metadata", "variant", VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateVariant(self._machine_variant_name, self._machine_type_id))
|
||||
else:
|
||||
config.set("metadata", "variant", self._machine_variant_name)
|
||||
if self._material_name and self._type != "material":
|
||||
config.set("metadata", "material", self._material_name)
|
||||
|
||||
if self._settings:
|
||||
VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateSettings(self._settings)
|
||||
config.add_section("values")
|
||||
for key, value in self._settings.items():
|
||||
config.set("values", key, str(value))
|
||||
|
||||
if self._changed_settings_defaults:
|
||||
VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateSettings(self._changed_settings_defaults)
|
||||
config.add_section("defaults")
|
||||
for key, value in self._changed_settings_defaults.items():
|
||||
config.set("defaults", key, str(value))
|
||||
|
||||
if self._disabled_settings_defaults:
|
||||
disabled_settings_defaults = [VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateSettingName(setting)
|
||||
for setting in self._disabled_settings_defaults]
|
||||
config.add_section("disabled_defaults")
|
||||
disabled_defaults_string = str(disabled_settings_defaults[0]) #Must be at least 1 item, otherwise we wouldn't enter this if statement.
|
||||
for item in disabled_settings_defaults[1:]:
|
||||
disabled_defaults_string += "," + str(item)
|
||||
|
||||
output = io.StringIO()
|
||||
config.write(output)
|
||||
return self._filename, output.getvalue()
|
|
@ -0,0 +1,181 @@
|
|||
# Copyright (c) 2016 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
import configparser #To get version numbers from config files.
|
||||
|
||||
from UM.VersionUpgrade import VersionUpgrade # Superclass of the plugin.
|
||||
|
||||
from . import MachineInstance # To upgrade machine instances.
|
||||
from . import Preferences #To upgrade preferences.
|
||||
from . import Profile # To upgrade profiles.
|
||||
|
||||
## How to translate printer names from the old version to the new.
|
||||
_printer_translations = {
|
||||
"ultimaker2plus": "ultimaker2_plus"
|
||||
}
|
||||
|
||||
## How to translate profile names from the old version to the new.
|
||||
_profile_translations = {
|
||||
"PLA": "generic_pla",
|
||||
"ABS": "generic_abs",
|
||||
"CPE": "generic_cpe"
|
||||
}
|
||||
|
||||
## How to translate setting names from the old version to the new.
|
||||
_setting_name_translations = {
|
||||
"remove_overlapping_walls_0_enabled": "travel_compensate_overlapping_walls_0_enabled",
|
||||
"remove_overlapping_walls_enabled": "travel_compensate_overlapping_walls_enabled",
|
||||
"remove_overlapping_walls_x_enabled": "travel_compensate_overlapping_walls_x_enabled",
|
||||
"retraction_hop": "retraction_hop_enabled",
|
||||
"speed_support_lines": "speed_support_infill"
|
||||
}
|
||||
|
||||
## How to translate variants of specific machines from the old version to the
|
||||
# new.
|
||||
_variant_translations = {
|
||||
"ultimaker2_plus": {
|
||||
"0.25 mm": "ultimaker2_plus_0.25",
|
||||
"0.4 mm": "ultimaker2_plus_0.4",
|
||||
"0.6 mm": "ultimaker2_plus_0.6",
|
||||
"0.8 mm": "ultimaker2_plus_0.8"
|
||||
},
|
||||
"ultimaker2_extended_plus": {
|
||||
"0.25 mm": "ultimaker2_extended_plus_0.25",
|
||||
"0.4 mm": "ultimaker2_extended_plus_0.4",
|
||||
"0.6 mm": "ultimaker2_extended_plus_0.6",
|
||||
"0.8 mm": "ultimaker2_extended_plus_0.8"
|
||||
}
|
||||
}
|
||||
|
||||
## Converts configuration from Cura 2.1's file formats to Cura 2.2's.
|
||||
#
|
||||
# It converts the machine instances and profiles.
|
||||
class VersionUpgrade21to22(VersionUpgrade):
|
||||
## Gets the version number from a config file.
|
||||
#
|
||||
# In all config files that concern this version upgrade, the version
|
||||
# number is stored in general/version, so get the data from that key.
|
||||
#
|
||||
# \param serialised The contents of a config file.
|
||||
# \return \type{int} The version number of that config file.
|
||||
def getCfgVersion(self, serialised):
|
||||
parser = configparser.ConfigParser(interpolation = None)
|
||||
parser.read_string(serialised)
|
||||
return int(parser.get("general", "version")) #Explicitly give an exception when this fails. That means that the file format is not recognised.
|
||||
|
||||
## Converts machine instances from format version 1 to version 2.
|
||||
#
|
||||
# \param serialised The serialised machine instance in version 1.
|
||||
# \param filename The supposed file name of the machine instance, without
|
||||
# extension.
|
||||
# \return A tuple containing the new filename and the serialised machine
|
||||
# instance in version 2, or None if the input was not of the correct
|
||||
# format.
|
||||
def upgradeMachineInstance(self, serialised, filename):
|
||||
machine_instance = MachineInstance.importFrom(serialised, filename)
|
||||
if not machine_instance: #Invalid file format.
|
||||
return filename, None
|
||||
return machine_instance.export()
|
||||
|
||||
## Converts preferences from format version 2 to version 3.
|
||||
#
|
||||
# \param serialised The serialised preferences file in version 2.
|
||||
# \param filename THe supposed file name of the preferences file, without
|
||||
# extension.
|
||||
# \return A tuple containing the new filename and the serialised
|
||||
# preferences in version 3, or None if the input was not of the correct
|
||||
# format.
|
||||
def upgradePreferences(self, serialised, filename):
|
||||
preferences = Preferences.importFrom(serialised, filename)
|
||||
if not preferences: #Invalid file format.
|
||||
return filename, None
|
||||
return preferences.export()
|
||||
|
||||
## Converts profiles from format version 1 to version 2.
|
||||
#
|
||||
# \param serialised The serialised profile in version 1.
|
||||
# \param filename The supposed file name of the profile, without
|
||||
# extension.
|
||||
# \return A tuple containing the new filename and the serialised profile
|
||||
# in version 2, or None if the input was not of the correct format.
|
||||
def upgradeProfile(self, serialised, filename):
|
||||
profile = Profile.importFrom(serialised, filename)
|
||||
if not profile: # Invalid file format.
|
||||
return filename, None
|
||||
return profile.export()
|
||||
|
||||
## Translates a printer name that might have changed since the last
|
||||
# version.
|
||||
#
|
||||
# \param printer A printer name in Cura 2.1.
|
||||
# \return The name of the corresponding printer in Cura 2.2.
|
||||
@staticmethod
|
||||
def translatePrinter(printer):
|
||||
if printer in _printer_translations:
|
||||
return _printer_translations[printer]
|
||||
return printer #Doesn't need to be translated.
|
||||
|
||||
## Translates a built-in profile name that might have changed since the
|
||||
# last version.
|
||||
#
|
||||
# \param profile A profile name in the old version.
|
||||
# \return The corresponding profile name in the new version.
|
||||
@staticmethod
|
||||
def translateProfile(profile):
|
||||
if profile in _profile_translations:
|
||||
return _profile_translations[profile]
|
||||
return profile #Doesn't need to be translated.
|
||||
|
||||
## Updates settings for the change from Cura 2.1 to 2.2.
|
||||
#
|
||||
# The keys and values of settings are changed to what they should be in
|
||||
# the new version. Each setting is changed in-place in the provided
|
||||
# dictionary. This changes the input parameter.
|
||||
#
|
||||
# \param settings A dictionary of settings (as key-value pairs) to update.
|
||||
# \return The same dictionary.
|
||||
@staticmethod
|
||||
def translateSettings(settings):
|
||||
for key, value in settings.items():
|
||||
if key == "fill_perimeter_gaps": #Setting is removed.
|
||||
del settings[key]
|
||||
elif key == "remove_overlapping_walls_0_enabled": #Setting is functionally replaced.
|
||||
del settings[key]
|
||||
settings["travel_compensate_overlapping_walls_0_enabled"] = value
|
||||
elif key == "remove_overlapping_walls_enabled": #Setting is functionally replaced.
|
||||
del settings[key]
|
||||
settings["travel_compensate_overlapping_walls_enabled"] = value
|
||||
elif key == "remove_overlapping_walls_x_enabled": #Setting is functionally replaced.
|
||||
del settings[key]
|
||||
settings["travel_compensate_overlapping_walls_x_enabled"] = value
|
||||
elif key == "retraction_combing": #Combing was made into an enum instead of a boolean.
|
||||
settings[key] = "off" if (value == "False") else "all"
|
||||
elif key == "retraction_hop": #Setting key was changed.
|
||||
del settings[key]
|
||||
settings["retraction_hop_enabled"] = value
|
||||
elif key == "speed_support_lines": #Setting key was changed.
|
||||
del settings[key]
|
||||
settings["speed_support_infill"] = value
|
||||
return settings
|
||||
|
||||
## Translates a setting name for the change from Cura 2.1 to 2.2.
|
||||
#
|
||||
# \param setting The name of a setting in Cura 2.1.
|
||||
# \return The name of the corresponding setting in Cura 2.2.
|
||||
@staticmethod
|
||||
def translateSettingName(setting):
|
||||
if setting in _setting_name_translations:
|
||||
return _setting_name_translations[setting]
|
||||
return setting #Doesn't need to be translated.
|
||||
|
||||
## Translates a variant name for the change from Cura 2.1 to 2.2
|
||||
#
|
||||
# \param variant The name of a variant in Cura 2.1.
|
||||
# \param machine The name of the machine this variant is part of in Cura
|
||||
# 2.2's naming.
|
||||
# \return The name of the corresponding variant in Cura 2.2.
|
||||
@staticmethod
|
||||
def translateVariant(variant, machine):
|
||||
if machine in _variant_translations and variant in _variant_translations[machine]:
|
||||
return _variant_translations[machine][variant]
|
||||
return variant
|
43
plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py
Normal file
43
plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py
Normal file
|
@ -0,0 +1,43 @@
|
|||
# Copyright (c) 2016 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from . import VersionUpgrade21to22
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
upgrade = VersionUpgrade21to22.VersionUpgrade21to22()
|
||||
|
||||
def getMetaData():
|
||||
return {
|
||||
"plugin": {
|
||||
"name": catalog.i18nc("@label", "Version Upgrade 2.1 to 2.2"),
|
||||
"author": "Ultimaker",
|
||||
"version": "1.0",
|
||||
"description": catalog.i18nc("@info:whatsthis", "Upgrades configurations from Cura 2.1 to Cura 2.2."),
|
||||
"api": 3
|
||||
},
|
||||
"version_upgrade": {
|
||||
# From To Upgrade function
|
||||
("profile", 1): ("quality", 2, upgrade.upgradeProfile),
|
||||
("machine_instance", 1): ("machine_stack", 2, upgrade.upgradeMachineInstance),
|
||||
("preferences", 2): ("preferences", 3, upgrade.upgradePreferences)
|
||||
},
|
||||
"sources": {
|
||||
"profile": {
|
||||
"get_version": upgrade.getCfgVersion,
|
||||
"location": {"./profiles", "./instance_profiles"}
|
||||
},
|
||||
"machine_instance": {
|
||||
"get_version": upgrade.getCfgVersion,
|
||||
"location": {"./machine_instances"}
|
||||
},
|
||||
"preferences": {
|
||||
"get_version": upgrade.getCfgVersion,
|
||||
"location": {"."}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def register(app):
|
||||
return { "version_upgrade": upgrade }
|
|
@ -3,32 +3,230 @@
|
|||
|
||||
import math
|
||||
import copy
|
||||
import io
|
||||
import xml.etree.ElementTree as ET
|
||||
import uuid
|
||||
|
||||
from UM.Logger import Logger
|
||||
|
||||
import UM.Dictionary
|
||||
|
||||
import UM.Settings
|
||||
|
||||
# The namespace is prepended to the tag name but between {}.
|
||||
# We are only interested in the actual tag name, so discard everything
|
||||
# before the last }
|
||||
def _tag_without_namespace(element):
|
||||
return element.tag[element.tag.rfind("}") + 1:]
|
||||
|
||||
## Handles serializing and deserializing material containers from an XML file
|
||||
class XmlMaterialProfile(UM.Settings.InstanceContainer):
|
||||
def __init__(self, container_id, *args, **kwargs):
|
||||
super().__init__(container_id, *args, **kwargs)
|
||||
|
||||
def serialize(self):
|
||||
raise NotImplementedError("Writing material profiles has not yet been implemented")
|
||||
## Overridden from InstanceContainer
|
||||
def duplicate(self, new_id, new_name = None):
|
||||
base_file = self.getMetaDataEntry("base_file", None)
|
||||
new_uuid = str(uuid.uuid4())
|
||||
|
||||
if base_file:
|
||||
containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = base_file)
|
||||
if containers:
|
||||
new_basefile = containers[0].duplicate(self.getMetaDataEntry("brand") + "_" + new_id, new_name)
|
||||
new_basefile.setMetaDataEntry("GUID", new_uuid)
|
||||
base_file = new_basefile.id
|
||||
UM.Settings.ContainerRegistry.getInstance().addContainer(new_basefile)
|
||||
|
||||
new_id = self.getMetaDataEntry("brand") + "_" + new_id + "_" + self.getDefinition().getId()
|
||||
variant = self.getMetaDataEntry("variant")
|
||||
if variant:
|
||||
variant_containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = variant)
|
||||
if variant_containers:
|
||||
new_id += "_" + variant_containers[0].getName().replace(" ", "_")
|
||||
|
||||
result = super().duplicate(new_id, new_name)
|
||||
result.setMetaDataEntry("GUID", new_uuid)
|
||||
if result.getMetaDataEntry("base_file", None):
|
||||
result.setMetaDataEntry("base_file", base_file)
|
||||
return result
|
||||
|
||||
## Overridden from InstanceContainer
|
||||
def setReadOnly(self, read_only):
|
||||
super().setReadOnly(read_only)
|
||||
|
||||
for container in UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(GUID = self.getMetaDataEntry("GUID")):
|
||||
container._read_only = read_only
|
||||
|
||||
## Overridden from InstanceContainer
|
||||
def setMetaDataEntry(self, key, value):
|
||||
if self.isReadOnly():
|
||||
return
|
||||
|
||||
super().setMetaDataEntry(key, value)
|
||||
|
||||
if key == "material":
|
||||
self.setName(value)
|
||||
|
||||
for container in UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(GUID = self.getMetaDataEntry("GUID")):
|
||||
container.setMetaData(copy.deepcopy(self._metadata))
|
||||
if key == "material":
|
||||
container.setName(value)
|
||||
|
||||
## Overridden from InstanceContainer
|
||||
def setProperty(self, key, property_name, property_value, container = None):
|
||||
if self.isReadOnly():
|
||||
return
|
||||
|
||||
super().setProperty(key, property_name, property_value)
|
||||
|
||||
for container in UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(GUID = self.getMetaDataEntry("GUID")):
|
||||
container._dirty = True
|
||||
|
||||
## Overridden from InstanceContainer
|
||||
def serialize(self):
|
||||
registry = UM.Settings.ContainerRegistry.getInstance()
|
||||
|
||||
base_file = self.getMetaDataEntry("base_file", "")
|
||||
if base_file and self.id != base_file:
|
||||
# Since we create an instance of XmlMaterialProfile for each machine and nozzle in the profile,
|
||||
# we should only serialize the "base" material definition, since that can then take care of
|
||||
# serializing the machine/nozzle specific profiles.
|
||||
raise NotImplementedError("Cannot serialize non-root XML materials")
|
||||
|
||||
builder = ET.TreeBuilder()
|
||||
|
||||
root = builder.start("fdmmaterial", { "xmlns": "http://www.ultimaker.com/material"})
|
||||
|
||||
## Begin Metadata Block
|
||||
builder.start("metadata")
|
||||
|
||||
metadata = copy.deepcopy(self.getMetaData())
|
||||
properties = metadata.pop("properties", {})
|
||||
|
||||
# Metadata properties that should not be serialized.
|
||||
metadata.pop("status", "")
|
||||
metadata.pop("variant", "")
|
||||
metadata.pop("type", "")
|
||||
metadata.pop("base_file", "")
|
||||
|
||||
## Begin Name Block
|
||||
builder.start("name")
|
||||
|
||||
builder.start("brand")
|
||||
builder.data(metadata.pop("brand", ""))
|
||||
builder.end("brand")
|
||||
|
||||
builder.start("material")
|
||||
builder.data(metadata.pop("material", ""))
|
||||
builder.end("material")
|
||||
|
||||
builder.start("color")
|
||||
builder.data(metadata.pop("color_name", ""))
|
||||
builder.end("color")
|
||||
|
||||
builder.end("name")
|
||||
## End Name Block
|
||||
|
||||
for key, value in metadata.items():
|
||||
builder.start(key)
|
||||
builder.data(value)
|
||||
builder.end(key)
|
||||
|
||||
builder.end("metadata")
|
||||
## End Metadata Block
|
||||
|
||||
## Begin Properties Block
|
||||
builder.start("properties")
|
||||
|
||||
for key, value in properties.items():
|
||||
builder.start(key)
|
||||
builder.data(value)
|
||||
builder.end(key)
|
||||
|
||||
builder.end("properties")
|
||||
## End Properties Block
|
||||
|
||||
## Begin Settings Block
|
||||
builder.start("settings")
|
||||
|
||||
if self.getDefinition().id == "fdmprinter":
|
||||
for instance in self.findInstances():
|
||||
self._addSettingElement(builder, instance)
|
||||
|
||||
machine_container_map = {}
|
||||
machine_nozzle_map = {}
|
||||
|
||||
all_containers = registry.findInstanceContainers(GUID = self.getMetaDataEntry("GUID"))
|
||||
for container in all_containers:
|
||||
definition_id = container.getDefinition().id
|
||||
if definition_id == "fdmprinter":
|
||||
continue
|
||||
|
||||
if definition_id not in machine_container_map:
|
||||
machine_container_map[definition_id] = container
|
||||
|
||||
if definition_id not in machine_nozzle_map:
|
||||
machine_nozzle_map[definition_id] = {}
|
||||
|
||||
variant = container.getMetaDataEntry("variant")
|
||||
if variant:
|
||||
machine_nozzle_map[definition_id][variant] = container
|
||||
continue
|
||||
|
||||
machine_container_map[definition_id] = container
|
||||
|
||||
for definition_id, container in machine_container_map.items():
|
||||
definition = container.getDefinition()
|
||||
try:
|
||||
product = UM.Dictionary.findKey(self.__product_id_map, definition_id)
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
builder.start("machine")
|
||||
builder.start("machine_identifier", { "manufacturer": definition.getMetaDataEntry("manufacturer", ""), "product": product})
|
||||
builder.end("machine_identifier")
|
||||
|
||||
for instance in container.findInstances():
|
||||
if self.getDefinition().id == "fdmprinter" and self.getInstance(instance.definition.key) and self.getProperty(instance.definition.key, "value") == instance.value:
|
||||
# If the settings match that of the base profile, just skip since we inherit the base profile.
|
||||
continue
|
||||
|
||||
self._addSettingElement(builder, instance)
|
||||
|
||||
# Find all hotend sub-profiles corresponding to this material and machine and add them to this profile.
|
||||
for hotend_id, hotend in machine_nozzle_map[definition_id].items():
|
||||
variant_containers = registry.findInstanceContainers(id = hotend.getMetaDataEntry("variant"))
|
||||
if not variant_containers:
|
||||
continue
|
||||
|
||||
builder.start("hotend", { "id": variant_containers[0].getName() })
|
||||
|
||||
for instance in hotend.findInstances():
|
||||
if container.getInstance(instance.definition.key) and container.getProperty(instance.definition.key, "value") == instance.value:
|
||||
# If the settings match that of the machine profile, just skip since we inherit the machine profile.
|
||||
continue
|
||||
|
||||
self._addSettingElement(builder, instance)
|
||||
|
||||
builder.end("hotend")
|
||||
|
||||
builder.end("machine")
|
||||
|
||||
builder.end("settings")
|
||||
## End Settings Block
|
||||
|
||||
builder.end("fdmmaterial")
|
||||
|
||||
root = builder.close()
|
||||
_indent(root)
|
||||
stream = io.StringIO()
|
||||
tree = ET.ElementTree(root)
|
||||
tree.write(stream, "unicode", True)
|
||||
|
||||
return stream.getvalue()
|
||||
|
||||
## Overridden from InstanceContainer
|
||||
def deserialize(self, serialized):
|
||||
data = ET.fromstring(serialized)
|
||||
|
||||
self.addMetaDataEntry("type", "material")
|
||||
|
||||
# TODO: Add material verfication
|
||||
self.addMetaDataEntry("status", "Unknown")
|
||||
self.addMetaDataEntry("status", "unknown")
|
||||
|
||||
metadata = data.iterfind("./um:metadata/*", self.__namespaces)
|
||||
for entry in metadata:
|
||||
|
@ -39,7 +237,7 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer):
|
|||
material = entry.find("./um:material", self.__namespaces)
|
||||
color = entry.find("./um:color", self.__namespaces)
|
||||
|
||||
self.setName("{0} {1} ({2})".format(brand.text, material.text, color.text))
|
||||
self.setName(material.text)
|
||||
|
||||
self.addMetaDataEntry("brand", brand.text)
|
||||
self.addMetaDataEntry("material", material.text)
|
||||
|
@ -49,6 +247,12 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer):
|
|||
|
||||
self.addMetaDataEntry(tag_name, entry.text)
|
||||
|
||||
if not "description" in self.getMetaData():
|
||||
self.addMetaDataEntry("description", "")
|
||||
|
||||
if not "adhesion_info" in self.getMetaData():
|
||||
self.addMetaDataEntry("adhesion_info", "")
|
||||
|
||||
property_values = {}
|
||||
properties = data.iterfind("./um:properties/*", self.__namespaces)
|
||||
for entry in properties:
|
||||
|
@ -58,17 +262,6 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer):
|
|||
diameter = float(property_values.get("diameter", 2.85)) # In mm
|
||||
density = float(property_values.get("density", 1.3)) # In g/cm3
|
||||
|
||||
weight_per_cm = (math.pi * (diameter / 20) ** 2 * 0.1) * density
|
||||
|
||||
spool_weight = property_values.get("spool_weight")
|
||||
spool_length = property_values.get("spool_length")
|
||||
if spool_weight:
|
||||
length = float(spool_weight) / weight_per_cm
|
||||
property_values["spool_length"] = str(length / 100)
|
||||
elif spool_length:
|
||||
weight = (float(spool_length) * 100) * weight_per_cm
|
||||
property_values["spool_weight"] = str(weight)
|
||||
|
||||
self.addMetaDataEntry("properties", property_values)
|
||||
|
||||
self.setDefinition(UM.Settings.ContainerRegistry.getInstance().findDefinitionContainers(id = "fdmprinter")[0])
|
||||
|
@ -83,6 +276,8 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer):
|
|||
else:
|
||||
Logger.log("d", "Unsupported material setting %s", key)
|
||||
|
||||
self._dirty = False
|
||||
|
||||
machines = data.iterfind("./um:settings/um:machine", self.__namespaces)
|
||||
for machine in machines:
|
||||
machine_setting_values = {}
|
||||
|
@ -112,6 +307,7 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer):
|
|||
new_material.setName(self.getName())
|
||||
new_material.setMetaData(copy.deepcopy(self.getMetaData()))
|
||||
new_material.setDefinition(definition)
|
||||
new_material.addMetaDataEntry("base_file", self.id)
|
||||
|
||||
for key, value in global_setting_values.items():
|
||||
new_material.setProperty(key, "value", value, definition)
|
||||
|
@ -142,6 +338,7 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer):
|
|||
new_hotend_material.setName(self.getName())
|
||||
new_hotend_material.setMetaData(copy.deepcopy(self.getMetaData()))
|
||||
new_hotend_material.setDefinition(definition)
|
||||
new_hotend_material.addMetaDataEntry("base_file", self.id)
|
||||
|
||||
new_hotend_material.addMetaDataEntry("variant", variant_containers[0].id)
|
||||
|
||||
|
@ -162,6 +359,15 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer):
|
|||
new_hotend_material._dirty = False
|
||||
UM.Settings.ContainerRegistry.getInstance().addContainer(new_hotend_material)
|
||||
|
||||
def _addSettingElement(self, builder, instance):
|
||||
try:
|
||||
key = UM.Dictionary.findKey(self.__material_property_setting_map, instance.definition.key)
|
||||
except ValueError:
|
||||
return
|
||||
|
||||
builder.start("setting", { "key": key })
|
||||
builder.data(str(instance.value))
|
||||
builder.end("setting")
|
||||
|
||||
# Map XML file setting names to internal names
|
||||
__material_property_setting_map = {
|
||||
|
@ -174,6 +380,7 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer):
|
|||
}
|
||||
|
||||
# Map XML file product names to internal ids
|
||||
# TODO: Move this to definition's metadata
|
||||
__product_id_map = {
|
||||
"Ultimaker2": "ultimaker2",
|
||||
"Ultimaker2+": "ultimaker2_plus",
|
||||
|
@ -184,6 +391,30 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer):
|
|||
"Ultimaker Original+": "ultimaker_original_plus"
|
||||
}
|
||||
|
||||
# Map of recognised namespaces with a proper prefix.
|
||||
__namespaces = {
|
||||
"um": "http://www.ultimaker.com/material"
|
||||
}
|
||||
|
||||
## Helper function for pretty-printing XML because ETree is stupid
|
||||
def _indent(elem, level = 0):
|
||||
i = "\n" + level * " "
|
||||
if len(elem):
|
||||
if not elem.text or not elem.text.strip():
|
||||
elem.text = i + " "
|
||||
if not elem.tail or not elem.tail.strip():
|
||||
elem.tail = i
|
||||
for elem in elem:
|
||||
_indent(elem, level + 1)
|
||||
if not elem.tail or not elem.tail.strip():
|
||||
elem.tail = i
|
||||
else:
|
||||
if level and (not elem.tail or not elem.tail.strip()):
|
||||
elem.tail = i
|
||||
|
||||
|
||||
# The namespace is prepended to the tag name but between {}.
|
||||
# We are only interested in the actual tag name, so discard everything
|
||||
# before the last }
|
||||
def _tag_without_namespace(element):
|
||||
return element.tag[element.tag.rfind("}") + 1:]
|
||||
|
|
|
@ -17,6 +17,7 @@ def getMetaData():
|
|||
"api": 3
|
||||
},
|
||||
"settings_container": {
|
||||
"type": "material",
|
||||
"mimetype": "application/x-ultimaker-material-profile"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
},
|
||||
|
||||
"overrides": {
|
||||
"machine_start_gcode": { "default_value": "; -- START GCODE --\nM800 ; Custom GCODE to fire start print procedure\n; -- end of START GCODE --" },
|
||||
"machine_start_gcode": { "default_value": "; -- START GCODE --\nM104 S{material_print_temperature} ; Heat up extruder while leveling\nM800 ; Custom GCODE to fire start print procedure\nM109 S{material_print_temperature} ; Makes sure the temperature is correct before printing\n; -- end of START GCODE --" },
|
||||
"machine_end_gcode": { "default_value": "; -- END GCODE --\nM801 ; Custom GCODE to fire end print procedure\n; -- end of END GCODE --" },
|
||||
"machine_width": { "default_value": 210 },
|
||||
"machine_depth": { "default_value": 297 },
|
||||
|
|
|
@ -144,6 +144,42 @@
|
|||
"settable_per_extruder": true,
|
||||
"settable_per_meshgroup": false,
|
||||
"settable_globally": false
|
||||
},
|
||||
"extruder_prime_pos_x":
|
||||
{
|
||||
"label": "Extruder Prime X Position",
|
||||
"description": "The X coordinate of the position where the nozzle primes at the start of printing.",
|
||||
"type": "float",
|
||||
"unit": "mm",
|
||||
"default_value": 0,
|
||||
"minimum_value_warning": "-1000",
|
||||
"maximum_value_warning": "1000",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"extruder_prime_pos_y":
|
||||
{
|
||||
"label": "Extruder Prime Y Position",
|
||||
"description": "The Y coordinate of the position where the nozzle primes at the start of printing.",
|
||||
"type": "float",
|
||||
"unit": "mm",
|
||||
"default_value": 0,
|
||||
"minimum_value_warning": "-1000",
|
||||
"maximum_value_warning": "1000",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"extruder_prime_pos_z":
|
||||
{
|
||||
"label": "Extruder Prime Z Position",
|
||||
"description": "The Z coordinate of the position where the nozzle primes at the start of printing.",
|
||||
"type": "float",
|
||||
"unit": "mm",
|
||||
"default_value": 0,
|
||||
"minimum_value_warning": "-1000",
|
||||
"maximum_value_warning": "1000",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,19 +29,19 @@
|
|||
{
|
||||
"machine_show_variants":
|
||||
{
|
||||
"label": "Show machine variants",
|
||||
"description": "Whether to show the different variants of this machine, which are described in separate json files.",
|
||||
"default_value": false,
|
||||
"type": "bool",
|
||||
"label": "Show machine variants",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
"machine_start_gcode":
|
||||
{
|
||||
"label": "Start GCode",
|
||||
"description": "Gcode commands to be executed at the very start - separated by \\n.",
|
||||
"default_value": "G28 ;Home\nG1 Z15.0 F6000 ;Move the platform down 15mm\n;Prime the extruder\nG92 E0\nG1 F200 E3\nG92 E0",
|
||||
"label": "Start GCode",
|
||||
"type": "str",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
|
@ -49,9 +49,9 @@
|
|||
},
|
||||
"machine_end_gcode":
|
||||
{
|
||||
"label": "End GCode",
|
||||
"description": "Gcode commands to be executed at the very end - separated by \\n.",
|
||||
"default_value": "M104 S0\nM140 S0\n;Retract the filament\nG92 E1\nG1 E-1 F300\nG28 X0 Y0\nM84",
|
||||
"label": "End GCode",
|
||||
"type": "str",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
|
@ -59,8 +59,18 @@
|
|||
},
|
||||
"material_bed_temp_wait":
|
||||
{
|
||||
"description": "Whether to insert a command to wait until the bed temperature is reached at the start.",
|
||||
"label": "Wait for bed heatup",
|
||||
"description": "Whether to insert a command to wait until the bed temperature is reached at the start.",
|
||||
"default_value": true,
|
||||
"type": "bool",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
"material_print_temp_wait":
|
||||
{
|
||||
"label": "Wait for nozzle heatup",
|
||||
"description": "Whether to wait until the nozzle temperature is reached at the start.",
|
||||
"default_value": true,
|
||||
"type": "bool",
|
||||
"settable_per_mesh": false,
|
||||
|
@ -69,49 +79,49 @@
|
|||
},
|
||||
"material_print_temp_prepend":
|
||||
{
|
||||
"label": "Include material temperatures",
|
||||
"description": "Whether to include nozzle temperature commands at the start of the gcode. When the start_gcode already contains nozzle temperature commands Cura frontend will automatically disable this setting.",
|
||||
"default_value": true,
|
||||
"type": "bool",
|
||||
"label": "Wait for material heatup",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
"machine_width":
|
||||
{
|
||||
"label": "Machine width",
|
||||
"description": "The width (X-direction) of the printable area.",
|
||||
"default_value": 100,
|
||||
"type": "float",
|
||||
"label": "Machine width",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
"machine_depth":
|
||||
{
|
||||
"label": "Machine depth",
|
||||
"description": "The depth (Y-direction) of the printable area.",
|
||||
"default_value": 100,
|
||||
"type": "float",
|
||||
"label": "Machine depth",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
"machine_height":
|
||||
{
|
||||
"label": "Machine height",
|
||||
"description": "The height (Z-direction) of the printable area.",
|
||||
"default_value": 100,
|
||||
"type": "float",
|
||||
"label": "Machine height",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
"machine_heated_bed":
|
||||
{
|
||||
"label": "Has heated bed",
|
||||
"description": "Whether the machine has a heated bed present.",
|
||||
"default_value": false,
|
||||
"label": "Has heated bed",
|
||||
"type": "bool",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
|
@ -119,28 +129,28 @@
|
|||
},
|
||||
"machine_center_is_zero":
|
||||
{
|
||||
"label": "Is center origin",
|
||||
"description": "Whether the X/Y coordinates of the zero position of the printer is at the center of the printable area.",
|
||||
"default_value": false,
|
||||
"type": "bool",
|
||||
"label": "Is center origin",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
"machine_extruder_count":
|
||||
{
|
||||
"label": "Number extruders",
|
||||
"description": "Number of extruder trains. An extruder train is the combination of a feeder, bowden tube, and nozzle.",
|
||||
"default_value": 1,
|
||||
"type": "int",
|
||||
"label": "Number extruders",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
"machine_nozzle_tip_outer_diameter":
|
||||
{
|
||||
"description": "The outer diameter of the tip of the nozzle.",
|
||||
"label": "Outer nozzle diameter",
|
||||
"description": "The outer diameter of the tip of the nozzle.",
|
||||
"default_value": 1,
|
||||
"type": "float",
|
||||
"settable_per_mesh": false,
|
||||
|
@ -150,74 +160,87 @@
|
|||
},
|
||||
"machine_nozzle_head_distance":
|
||||
{
|
||||
"label": "Nozzle length",
|
||||
"description": "The height difference between the tip of the nozzle and the lowest part of the print head.",
|
||||
"default_value": 3,
|
||||
"type": "float",
|
||||
"label": "Nozzle length",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
"machine_nozzle_expansion_angle":
|
||||
{
|
||||
"label": "Nozzle angle",
|
||||
"description": "The angle between the horizontal plane and the conical part right above the tip of the nozzle.",
|
||||
"default_value": 45,
|
||||
"type": "int",
|
||||
"label": "Nozzle angle",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
"machine_heat_zone_length":
|
||||
{
|
||||
"label": "Heat zone length",
|
||||
"description": "The distance from the tip of the nozzle in which heat from the nozzle is transfered to the filament.",
|
||||
"default_value": 16,
|
||||
"type": "float",
|
||||
"label": "Heat zone length",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
"machine_nozzle_heat_up_speed":
|
||||
{
|
||||
"label": "Heat up speed",
|
||||
"description": "The speed (°C/s) by which the nozzle heats up averaged over the window of normal printing temperatures and the standby temperature.",
|
||||
"default_value": 2.0,
|
||||
"unit": "°C/s",
|
||||
"type": "float",
|
||||
"label": "Heat up speed",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"machine_nozzle_cool_down_speed":
|
||||
{
|
||||
"label": "Cool down speed",
|
||||
"description": "The speed (°C/s) by which the nozzle cools down averaged over the window of normal printing temperatures and the standby temperature.",
|
||||
"default_value": 2.0,
|
||||
"unit": "°C/s",
|
||||
"type": "float",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"machine_min_cool_heat_time_window":
|
||||
{
|
||||
"label": "Minimal Time Standby Temperature",
|
||||
"description": "The minimal time an extruder has to be inactive before the nozzle is cooled. Only when an extruder is not used for longer than this time will it be allowed to cool down to the standby temperature.",
|
||||
"default_value": 50.0,
|
||||
"unit": "s",
|
||||
"type": "float",
|
||||
"label": "Cool down speed",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"machine_gcode_flavor":
|
||||
{
|
||||
"label": "Gcode flavour",
|
||||
"description": "The type of gcode to be generated.",
|
||||
"default_value": "RepRap",
|
||||
"type": "str",
|
||||
"label": "Gcode flavour",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
"machine_disallowed_areas":
|
||||
{
|
||||
"label": "Disallowed areas",
|
||||
"description": "A list of polygons with areas the print head is not allowed to enter.",
|
||||
"type": "polygons",
|
||||
"default_value": [],
|
||||
"label": "Disallowed areas",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
"machine_head_polygon":
|
||||
{
|
||||
"label": "Machine head polygon",
|
||||
"description": "A 2D silhouette of the print head (fan caps excluded).",
|
||||
"type": "polygon",
|
||||
"default_value":
|
||||
|
@ -239,13 +262,13 @@
|
|||
1
|
||||
]
|
||||
],
|
||||
"label": "Machine head polygon",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
"machine_head_with_fans_polygon":
|
||||
{
|
||||
"label": "Machine head & Fan polygon",
|
||||
"description": "A 2D silhouette of the print head (fan caps included).",
|
||||
"type": "polygon",
|
||||
"default_value":
|
||||
|
@ -267,16 +290,15 @@
|
|||
-10
|
||||
]
|
||||
],
|
||||
"label": "Machine head & Fan polygon",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
"gantry_height":
|
||||
{
|
||||
"label": "Gantry height",
|
||||
"description": "The height difference between the tip of the nozzle and the gantry system (X and Y axes).",
|
||||
"default_value": 99999999999,
|
||||
"label": "Gantry height",
|
||||
"type": "float",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
|
@ -303,6 +325,51 @@
|
|||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
"extruder_prime_pos_x":
|
||||
{
|
||||
"label": "Extruder Prime X Position",
|
||||
"description": "The X coordinate of the position where the nozzle primes at the start of printing.",
|
||||
"type": "float",
|
||||
"unit": "mm",
|
||||
"default_value": 0,
|
||||
"minimum_value_warning": "-1000",
|
||||
"maximum_value_warning": "1000",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"extruder_prime_pos_y":
|
||||
{
|
||||
"label": "Extruder Prime Y Position",
|
||||
"description": "The Y coordinate of the position where the nozzle primes at the start of printing.",
|
||||
"type": "float",
|
||||
"unit": "mm",
|
||||
"default_value": 0,
|
||||
"minimum_value_warning": "-1000",
|
||||
"maximum_value_warning": "1000",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"extruder_prime_pos_z":
|
||||
{
|
||||
"label": "Extruder Prime Z Position",
|
||||
"description": "The Z coordinate of the position where the nozzle primes at the start of printing.",
|
||||
"type": "float",
|
||||
"unit": "mm",
|
||||
"default_value": 0,
|
||||
"minimum_value_warning": "-1000",
|
||||
"maximum_value_warning": "1000",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"extruder_prime_pos_abs":
|
||||
{
|
||||
"label": "Absolute Extruder Prime Position",
|
||||
"description": "Make the extruder prime position absolute rather than relative to the last-known location of the head.",
|
||||
"type": "bool",
|
||||
"default_value": false,
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -398,7 +465,7 @@
|
|||
},
|
||||
"skin_line_width":
|
||||
{
|
||||
"label": "Top/bottom Line Width",
|
||||
"label": "Top/Bottom Line Width",
|
||||
"description": "Width of a single top/bottom line.",
|
||||
"unit": "mm",
|
||||
"minimum_value": "0.0001",
|
||||
|
@ -525,6 +592,7 @@
|
|||
"default_value": 0.8,
|
||||
"minimum_value": "0",
|
||||
"minimum_value_warning": "0.6",
|
||||
"maximum_value_warning": "machine_height",
|
||||
"type": "float",
|
||||
"settable_per_mesh": true,
|
||||
"children":
|
||||
|
@ -536,7 +604,7 @@
|
|||
"unit": "mm",
|
||||
"default_value": 0.8,
|
||||
"minimum_value": "0",
|
||||
"maximum_value_warning": "100",
|
||||
"maximum_value_warning": "machine_height",
|
||||
"type": "float",
|
||||
"value": "top_bottom_thickness",
|
||||
"settable_per_mesh": true,
|
||||
|
@ -564,6 +632,7 @@
|
|||
"minimum_value": "0",
|
||||
"type": "float",
|
||||
"value": "top_bottom_thickness",
|
||||
"maximum_value_warning": "machine_height",
|
||||
"settable_per_mesh": true,
|
||||
"children":
|
||||
{
|
||||
|
@ -705,7 +774,7 @@
|
|||
"type": "float",
|
||||
"default_value": 2,
|
||||
"minimum_value": "0",
|
||||
"value": "0 if infill_sparse_density == 0 else (infill_line_width * 100) / infill_sparse_density * (2 if infill_pattern == \"grid\" else (3 if infill_pattern == \"triangles\" else 1))",
|
||||
"value": "0 if infill_sparse_density == 0 else (infill_line_width * 100) / infill_sparse_density * (2 if infill_pattern == \"grid\" else (3 if infill_pattern == \"triangles\" else (3 if infill_pattern == \"cubic\" else 1)))",
|
||||
"settable_per_mesh": true
|
||||
}
|
||||
}
|
||||
|
@ -713,12 +782,13 @@
|
|||
"infill_pattern":
|
||||
{
|
||||
"label": "Infill Pattern",
|
||||
"description": "The pattern of the infill material of the print. The line and zig zag infill swap direction on alternate layers, reducing material cost. The grid, triangle and concentric patterns are fully printed every layer.",
|
||||
"description": "The pattern of the infill material of the print. The line and zig zag infill swap direction on alternate layers, reducing material cost. The grid, cubic, triangle and concentric patterns are fully printed every layer.",
|
||||
"type": "enum",
|
||||
"options":
|
||||
{
|
||||
"grid": "Grid",
|
||||
"lines": "Lines",
|
||||
"cubic": "Cubic",
|
||||
"triangles": "Triangles",
|
||||
"concentric": "Concentric",
|
||||
"zigzag": "Zig Zag"
|
||||
|
@ -914,7 +984,8 @@
|
|||
"description": "Retract the filament when the nozzle is moving over a non-printed area. ",
|
||||
"type": "bool",
|
||||
"default_value": true,
|
||||
"settable_per_mesh": true
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"retraction_amount": {
|
||||
"label": "Retraction Distance",
|
||||
|
@ -925,7 +996,8 @@
|
|||
"minimum_value_warning": "-0.0001",
|
||||
"maximum_value_warning": "10.0",
|
||||
"enabled": "retraction_enable",
|
||||
"settable_per_mesh": true
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"retraction_speed": {
|
||||
"label": "Retraction Speed",
|
||||
|
@ -937,7 +1009,8 @@
|
|||
"maximum_value": "299792458000",
|
||||
"maximum_value_warning": "100",
|
||||
"enabled": "retraction_enable",
|
||||
"settable_per_mesh": true,
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true,
|
||||
"children": {
|
||||
"retraction_retract_speed": {
|
||||
"label": "Retraction Retract Speed",
|
||||
|
@ -950,7 +1023,8 @@
|
|||
"maximum_value_warning": "100",
|
||||
"enabled": "retraction_enable",
|
||||
"value": "retraction_speed",
|
||||
"settable_per_mesh": true
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"retraction_prime_speed": {
|
||||
"label": "Retraction Prime Speed",
|
||||
|
@ -963,7 +1037,8 @@
|
|||
"maximum_value_warning": "100",
|
||||
"enabled": "retraction_enable",
|
||||
"value": "retraction_speed",
|
||||
"settable_per_mesh": true
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -976,7 +1051,8 @@
|
|||
"minimum_value_warning": "-0.0001",
|
||||
"maximum_value_warning": "5.0",
|
||||
"enabled": "retraction_enable",
|
||||
"settable_per_mesh": true
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"retraction_min_travel": {
|
||||
"label": "Retraction Minimum Travel",
|
||||
|
@ -988,7 +1064,8 @@
|
|||
"minimum_value": "0",
|
||||
"maximum_value_warning": "10",
|
||||
"enabled": "retraction_enable",
|
||||
"settable_per_mesh": true
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"retraction_count_max": {
|
||||
"label": "Maximum Retraction Count",
|
||||
|
@ -998,7 +1075,8 @@
|
|||
"maximum_value_warning": "100",
|
||||
"type": "int",
|
||||
"enabled": "retraction_enable",
|
||||
"settable_per_mesh": true
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"retraction_extrusion_window": {
|
||||
"label": "Minimum Extrusion Distance Window",
|
||||
|
@ -1010,7 +1088,8 @@
|
|||
"maximum_value_warning": "retraction_amount * 2",
|
||||
"value": "retraction_amount",
|
||||
"enabled": "retraction_enable",
|
||||
"settable_per_mesh": true
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"retraction_hop_enabled": {
|
||||
"label": "Z Hop when Retracted",
|
||||
|
@ -1018,7 +1097,8 @@
|
|||
"type": "bool",
|
||||
"default_value": false,
|
||||
"enabled": "retraction_enable",
|
||||
"settable_per_mesh": true,
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true,
|
||||
"children": {
|
||||
"retraction_hop_only_when_collides": {
|
||||
"label": "Z Hop Only Over Printed Parts",
|
||||
|
@ -1026,7 +1106,8 @@
|
|||
"type": "bool",
|
||||
"default_value": false,
|
||||
"enabled": "retraction_enable and retraction_hop_enabled and travel_avoid_other_parts",
|
||||
"settable_per_mesh": true
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"retraction_hop": {
|
||||
"label": "Z Hop Height",
|
||||
|
@ -1037,7 +1118,8 @@
|
|||
"minimum_value_warning": "-0.0001",
|
||||
"maximum_value_warning": "10",
|
||||
"enabled": "retraction_enable and retraction_hop_enabled",
|
||||
"settable_per_mesh": true
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -1831,7 +1913,7 @@
|
|||
"unit": "mm",
|
||||
"type": "float",
|
||||
"default_value": 0.5,
|
||||
"value": "layer_height_0",
|
||||
"value": "0 if adhesion_type == \"raft\" else layer_height_0",
|
||||
"minimum_value": "0",
|
||||
"maximum_value_warning": "10.0",
|
||||
"settable_per_mesh": false,
|
||||
|
@ -2324,7 +2406,7 @@
|
|||
"description": "If the raft is enabled, this is the extra raft area around the object which is also given a raft. Increasing this margin will create a stronger raft while using more material and leaving less area for your print.",
|
||||
"unit": "mm",
|
||||
"type": "float",
|
||||
"default_value": 5,
|
||||
"default_value": 15,
|
||||
"minimum_value_warning": "0",
|
||||
"maximum_value_warning": "10",
|
||||
"enabled": "adhesion_type == \"raft\""
|
||||
|
@ -2374,6 +2456,7 @@
|
|||
"unit": "mm",
|
||||
"type": "float",
|
||||
"default_value": 0.1,
|
||||
"value": "layer_height",
|
||||
"minimum_value": "0",
|
||||
"maximum_value_warning": "2.0",
|
||||
"enabled": "adhesion_type == \"raft\"",
|
||||
|
@ -2386,7 +2469,8 @@
|
|||
"description": "Width of the lines in the top surface of the raft. These can be thin lines so that the top of the raft becomes smooth.",
|
||||
"unit": "mm",
|
||||
"type": "float",
|
||||
"default_value": 0.3,
|
||||
"default_value": 0.4,
|
||||
"value": "line_width",
|
||||
"minimum_value": "0.0001",
|
||||
"maximum_value_warning": "machine_nozzle_size * 2",
|
||||
"enabled": "adhesion_type == \"raft\"",
|
||||
|
@ -2399,7 +2483,7 @@
|
|||
"description": "The distance between the raft lines for the top raft layers. The spacing should be equal to the line width, so that the surface is solid.",
|
||||
"unit": "mm",
|
||||
"type": "float",
|
||||
"default_value": 0.3,
|
||||
"default_value": 0.4,
|
||||
"minimum_value": "0.0001",
|
||||
"maximum_value_warning": "5.0",
|
||||
"enabled": "adhesion_type == \"raft\"",
|
||||
|
@ -2413,7 +2497,8 @@
|
|||
"description": "Layer thickness of the middle raft layer.",
|
||||
"unit": "mm",
|
||||
"type": "float",
|
||||
"default_value": 0.27,
|
||||
"default_value": 0.15,
|
||||
"value": "layer_height * 1.5",
|
||||
"minimum_value": "0",
|
||||
"maximum_value_warning": "5.0",
|
||||
"enabled": "adhesion_type == \"raft\"",
|
||||
|
@ -2426,7 +2511,7 @@
|
|||
"description": "Width of the lines in the middle raft layer. Making the second layer extrude more causes the lines to stick to the bed.",
|
||||
"unit": "mm",
|
||||
"type": "float",
|
||||
"default_value": 1,
|
||||
"default_value": 0.7,
|
||||
"value": "line_width * 2",
|
||||
"minimum_value": "0.0001",
|
||||
"maximum_value_warning": "machine_nozzle_size * 2",
|
||||
|
@ -2440,7 +2525,8 @@
|
|||
"description": "The distance between the raft lines for the middle raft layer. The spacing of the middle should be quite wide, while being dense enough to support the top raft layers.",
|
||||
"unit": "mm",
|
||||
"type": "float",
|
||||
"default_value": 1.0,
|
||||
"default_value": 0.9,
|
||||
"value": "raft_interface_line_width + 0.2",
|
||||
"minimum_value": "0",
|
||||
"maximum_value_warning": "15.0",
|
||||
"enabled": "adhesion_type == \"raft\"",
|
||||
|
@ -2454,6 +2540,7 @@
|
|||
"unit": "mm",
|
||||
"type": "float",
|
||||
"default_value": 0.3,
|
||||
"value": "layer_height_0 * 1.2",
|
||||
"minimum_value": "0",
|
||||
"maximum_value_warning": "5.0",
|
||||
"enabled": "adhesion_type == \"raft\"",
|
||||
|
@ -2466,10 +2553,10 @@
|
|||
"description": "Width of the lines in the base raft layer. These should be thick lines to assist in bed adhesion.",
|
||||
"unit": "mm",
|
||||
"type": "float",
|
||||
"default_value": 1,
|
||||
"default_value": 0.8,
|
||||
"minimum_value": "0.0001",
|
||||
"value": "line_width * 2",
|
||||
"maximum_value_warning": "machine_nozzle_size * 2",
|
||||
"value": "machine_nozzle_size * 2",
|
||||
"maximum_value_warning": "machine_nozzle_size * 3",
|
||||
"enabled": "adhesion_type == \"raft\"",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
|
@ -2480,7 +2567,8 @@
|
|||
"description": "The distance between the raft lines for the base raft layer. Wide spacing makes for easy removal of the raft from the build plate.",
|
||||
"unit": "mm",
|
||||
"type": "float",
|
||||
"default_value": 3.0,
|
||||
"default_value": 1.6,
|
||||
"value": "raft_base_line_width * 2",
|
||||
"minimum_value": "0.0001",
|
||||
"maximum_value_warning": "100",
|
||||
"enabled": "adhesion_type == \"raft\"",
|
||||
|
@ -2524,12 +2612,12 @@
|
|||
"description": "The speed at which the middle raft layer is printed. This should be printed quite slowly, as the volume of material coming out of the nozzle is quite high.",
|
||||
"unit": "mm/s",
|
||||
"type": "float",
|
||||
"default_value": 20,
|
||||
"default_value": 15,
|
||||
"value": "raft_speed * 0.75",
|
||||
"minimum_value": "0.1",
|
||||
"maximum_value": "299792458000",
|
||||
"maximum_value_warning": "150",
|
||||
"enabled": "adhesion_type == \"raft\"",
|
||||
"value": "raft_speed",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
|
@ -2781,7 +2869,7 @@
|
|||
"type": "extruder",
|
||||
"default_value": "0",
|
||||
"value": "support_extruder_nr",
|
||||
"enabled": "support_enable",
|
||||
"enabled": "support_enable and support_roof_enable",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false
|
||||
}
|
||||
|
@ -2973,6 +3061,31 @@
|
|||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
"infill_mesh":
|
||||
{
|
||||
"label": "Infill Mesh",
|
||||
"description": "Use this mesh to modify the infill of other meshes with which it overlaps. Replaces infill regions of other meshes with regions for this mesh. It's suggested to only print one Wall and no Top/Bottom Skin for this mesh.",
|
||||
"type": "bool",
|
||||
"default_value": false,
|
||||
"settable_per_mesh": true,
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": false,
|
||||
"settable_globally": false
|
||||
},
|
||||
"infill_mesh_order":
|
||||
{
|
||||
"label": "Infill Mesh Order",
|
||||
"description": "Determines which infill mesh is inside the infill of another infill mesh. An infill mesh with a higher order will modify the infill of infill meshes with lower order and normal meshes.",
|
||||
"default_value": 0,
|
||||
"value": "1 if infill_mesh else 0",
|
||||
"minimum_value_warning": "1",
|
||||
"maximum_value_warning": "50",
|
||||
"type": "int",
|
||||
"settable_per_mesh": true,
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": false,
|
||||
"settable_globally": false
|
||||
},
|
||||
"magic_mesh_surface_mode":
|
||||
{
|
||||
"label": "Surface Mode",
|
||||
|
@ -3067,7 +3180,8 @@
|
|||
"description": "The maximum angle of overhangs after the they have been made printable. At a value of 0° all overhangs are replaced by a piece of model connected to the build plate, 90° will not change the model in any way.",
|
||||
"unit": "°",
|
||||
"type": "float",
|
||||
"minimum_value": "0",
|
||||
"minimum_value": "-89",
|
||||
"minimum_value_warning": "0",
|
||||
"maximum_value": "89",
|
||||
"default_value": 50,
|
||||
"enabled": "conical_overhang_enabled"
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
"overrides": {
|
||||
"machine_height": {
|
||||
"default_value": 315
|
||||
"default_value": 305
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
"overrides": {
|
||||
"machine_height": {
|
||||
"default_value": 313
|
||||
"default_value": 305
|
||||
},
|
||||
"machine_show_variants": {
|
||||
"default_value": true
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
"has_materials": true,
|
||||
"preferred_material": "*pla*",
|
||||
"preferred_quality": "*normal*",
|
||||
"supported_actions":["UMOCheckup", "UpgradeFirmware", "BedLevel"]
|
||||
"supported_actions":["UMOCheckup", "UpgradeFirmware", "BedLevel", "UMOUpgradeSelection"]
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
|
|
|
@ -9,7 +9,7 @@ Generic PLA profile. Serves as an example file, data in this file is not correct
|
|||
<material>ABS</material>
|
||||
<color>Generic</color>
|
||||
</name>
|
||||
<GUID>506c9f0d-e3aa-4bd4-b2d2-23e2425b1aa9</GUID>
|
||||
<GUID>60636bb4-518f-42e7-8237-fe77b194ebe0</GUID>
|
||||
<version>0</version>
|
||||
<color_code>#FF0000</color_code>
|
||||
</metadata>
|
||||
|
|
|
@ -9,7 +9,7 @@ Generic PLA profile. Serves as an example file, data in this file is not correct
|
|||
<material>CPE</material>
|
||||
<color>Generic</color>
|
||||
</name>
|
||||
<GUID>506c9f0d-e3aa-4bd4-b2d2-23e2425b1aa9</GUID>
|
||||
<GUID>12f41353-1a33-415e-8b4f-a775a6c70cc6</GUID>
|
||||
<version>0</version>
|
||||
<color_code>#0000FF</color_code>
|
||||
</metadata>
|
||||
|
|
|
@ -39,6 +39,8 @@ Item
|
|||
property alias resetProfile: resetProfileAction;
|
||||
property alias manageProfiles: manageProfilesAction;
|
||||
|
||||
property alias manageMaterials: manageMaterialsAction;
|
||||
|
||||
property alias preferences: preferencesAction;
|
||||
|
||||
property alias showEngineLog: showEngineLogAction;
|
||||
|
@ -90,7 +92,7 @@ Item
|
|||
Action
|
||||
{
|
||||
id: preferencesAction;
|
||||
text: catalog.i18nc("@action:inmenu menubar:settings","&Preferences...");
|
||||
text: catalog.i18nc("@action:inmenu","Configure Cura...");
|
||||
iconName: "configure";
|
||||
}
|
||||
|
||||
|
@ -107,6 +109,13 @@ Item
|
|||
iconName: "configure";
|
||||
}
|
||||
|
||||
Action
|
||||
{
|
||||
id: manageMaterialsAction
|
||||
text: catalog.i18nc("@action:inmenu", "Manage Materials...")
|
||||
iconName: "configure"
|
||||
}
|
||||
|
||||
Action
|
||||
{
|
||||
id: updateProfileAction;
|
||||
|
@ -273,5 +282,6 @@ Item
|
|||
{
|
||||
id: configureSettingVisibilityAction
|
||||
text: catalog.i18nc("@action:menu", "Configure setting visiblity...");
|
||||
iconName: "configure"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,8 @@ import QtQuick.Dialogs 1.1
|
|||
import UM 1.2 as UM
|
||||
import Cura 1.0 as Cura
|
||||
|
||||
import "Menus"
|
||||
|
||||
UM.MainWindow
|
||||
{
|
||||
id: base
|
||||
|
@ -55,41 +57,13 @@ UM.MainWindow
|
|||
Menu
|
||||
{
|
||||
id: fileMenu
|
||||
//: File menu
|
||||
title: catalog.i18nc("@title:menu menubar:toplevel","&File");
|
||||
|
||||
MenuItem {
|
||||
action: Cura.Actions.open;
|
||||
}
|
||||
|
||||
Menu
|
||||
{
|
||||
id: recentFilesMenu;
|
||||
title: catalog.i18nc("@title:menu menubar:file", "Open &Recent")
|
||||
iconName: "document-open-recent";
|
||||
|
||||
enabled: Printer.recentFiles.length > 0;
|
||||
|
||||
Instantiator
|
||||
{
|
||||
model: Printer.recentFiles
|
||||
MenuItem
|
||||
{
|
||||
text:
|
||||
{
|
||||
var path = modelData.toString()
|
||||
return (index + 1) + ". " + path.slice(path.lastIndexOf("/") + 1);
|
||||
}
|
||||
onTriggered: {
|
||||
UM.MeshFileHandler.readLocalFile(modelData);
|
||||
var meshName = backgroundItem.getMeshName(modelData.toString())
|
||||
backgroundItem.hasMesh(decodeURIComponent(meshName))
|
||||
}
|
||||
}
|
||||
onObjectAdded: recentFilesMenu.insertItem(index, object)
|
||||
onObjectRemoved: recentFilesMenu.removeItem(object)
|
||||
}
|
||||
}
|
||||
RecentFilesMenu { }
|
||||
|
||||
MenuSeparator { }
|
||||
|
||||
|
@ -130,7 +104,6 @@ UM.MainWindow
|
|||
|
||||
Menu
|
||||
{
|
||||
//: Edit menu
|
||||
title: catalog.i18nc("@title:menu menubar:toplevel","&Edit");
|
||||
|
||||
MenuItem { action: Cura.Actions.undo; }
|
||||
|
@ -146,173 +119,45 @@ UM.MainWindow
|
|||
MenuItem { action: Cura.Actions.unGroupObjects;}
|
||||
}
|
||||
|
||||
ViewMenu { title: catalog.i18nc("@title:menu", "&View") }
|
||||
|
||||
Menu
|
||||
{
|
||||
title: catalog.i18nc("@title:menu menubar:toplevel","&View");
|
||||
id: top_view_menu
|
||||
Instantiator
|
||||
{
|
||||
model: UM.ViewModel { }
|
||||
MenuItem
|
||||
{
|
||||
text: model.name;
|
||||
checkable: true;
|
||||
checked: model.active;
|
||||
exclusiveGroup: view_menu_top_group;
|
||||
onTriggered: UM.Controller.setActiveView(model.id);
|
||||
}
|
||||
onObjectAdded: top_view_menu.insertItem(index, object)
|
||||
onObjectRemoved: top_view_menu.removeItem(object)
|
||||
}
|
||||
ExclusiveGroup { id: view_menu_top_group; }
|
||||
}
|
||||
Menu
|
||||
{
|
||||
id: machineMenu;
|
||||
//: Machine menu
|
||||
title: catalog.i18nc("@title:menu menubar:toplevel","&Printer");
|
||||
id: settingsMenu
|
||||
title: catalog.i18nc("@title:menu", "&Settings")
|
||||
|
||||
PrinterMenu { title: catalog.i18nc("@title:menu menubar:toplevel", "&Printer") }
|
||||
|
||||
Instantiator
|
||||
{
|
||||
model: UM.ContainerStacksModel
|
||||
{
|
||||
filter: {"type": "machine"}
|
||||
}
|
||||
MenuItem
|
||||
{
|
||||
text: model.name;
|
||||
checkable: true;
|
||||
checked: Cura.MachineManager.activeMachineId == model.id
|
||||
exclusiveGroup: machineMenuGroup;
|
||||
onTriggered: Cura.MachineManager.setActiveMachine(model.id);
|
||||
}
|
||||
onObjectAdded: machineMenu.insertItem(index, object)
|
||||
onObjectRemoved: machineMenu.removeItem(object)
|
||||
}
|
||||
model: Cura.ExtrudersModel { }
|
||||
Menu {
|
||||
title: model.name
|
||||
|
||||
ExclusiveGroup { id: machineMenuGroup; }
|
||||
NozzleMenu { title: catalog.i18nc("@title:menu", "&Nozzle"); visible: Cura.MachineManager.hasVariants }
|
||||
MaterialMenu { title: catalog.i18nc("@title:menu", "&Material"); visible: Cura.MachineManager.hasMaterials }
|
||||
ProfileMenu { title: catalog.i18nc("@title:menu", "&Profile"); }
|
||||
|
||||
MenuSeparator { }
|
||||
|
||||
Instantiator
|
||||
{
|
||||
model: UM.InstanceContainersModel
|
||||
{
|
||||
filter:
|
||||
{
|
||||
"type": "variant",
|
||||
"definition": Cura.MachineManager.activeDefinitionId //Only show variants of this machine
|
||||
MenuItem { text: "Set as Active Extruder" }
|
||||
}
|
||||
}
|
||||
MenuItem {
|
||||
text: model.name;
|
||||
checkable: true;
|
||||
checked: model.id == Cura.MachineManager.activeVariantId;
|
||||
exclusiveGroup: machineVariantsGroup;
|
||||
onTriggered: Cura.MachineManager.setActiveVariant(model.id)
|
||||
}
|
||||
onObjectAdded: machineMenu.insertItem(index, object)
|
||||
onObjectRemoved: machineMenu.removeItem(object)
|
||||
onObjectAdded: settingsMenu.insertItem(index, object)
|
||||
onObjectRemoved: settingsMenu.removeItem(object)
|
||||
}
|
||||
|
||||
ExclusiveGroup { id: machineVariantsGroup; }
|
||||
NozzleMenu { title: catalog.i18nc("@title:menu", "&Nozzle"); visible: machineExtruderCount.properties.value <= 1 && Cura.MachineManager.hasVariants }
|
||||
MaterialMenu { title: catalog.i18nc("@title:menu", "&Material"); visible: machineExtruderCount.properties.value <= 1 && Cura.MachineManager.hasMaterials }
|
||||
ProfileMenu { title: catalog.i18nc("@title:menu", "&Profile"); visible: machineExtruderCount.properties.value <= 1 }
|
||||
|
||||
MenuSeparator { visible: Cura.MachineManager.hasVariants; }
|
||||
|
||||
MenuItem { action: Cura.Actions.addMachine; }
|
||||
MenuItem { action: Cura.Actions.configureMachines; }
|
||||
}
|
||||
|
||||
Menu
|
||||
{
|
||||
id: profileMenu
|
||||
title: catalog.i18nc("@title:menu menubar:toplevel", "P&rofile")
|
||||
|
||||
Instantiator
|
||||
{
|
||||
id: profileMenuInstantiator
|
||||
model: UM.InstanceContainersModel
|
||||
{
|
||||
filter:
|
||||
{
|
||||
var result = { "type": "quality" };
|
||||
if(Cura.MachineManager.filterQualityByMachine)
|
||||
{
|
||||
result.definition = Cura.MachineManager.activeDefinitionId;
|
||||
if(Cura.MachineManager.hasMaterials)
|
||||
{
|
||||
result.material = Cura.MachineManager.activeMaterialId;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result.definition = "fdmprinter"
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
property int separatorIndex: -1
|
||||
|
||||
Loader {
|
||||
property QtObject model_data: model
|
||||
property int model_index: index
|
||||
sourceComponent: profileMenuItemDelegate
|
||||
}
|
||||
|
||||
onObjectAdded:
|
||||
{
|
||||
//Insert a separator between readonly and custom profiles
|
||||
if(separatorIndex < 0 && index > 0) {
|
||||
if(model.getItem(index-1).readOnly != model.getItem(index).readOnly) {
|
||||
profileMenu.insertSeparator(index);
|
||||
separatorIndex = index;
|
||||
}
|
||||
}
|
||||
//Because of the separator, custom profiles move one index lower
|
||||
profileMenu.insertItem((model.getItem(index).readOnly) ? index : index + 1, object.item);
|
||||
}
|
||||
onObjectRemoved:
|
||||
{
|
||||
//When adding a profile, the menu is rebuild by removing all items.
|
||||
//If a separator was added, we need to remove that too.
|
||||
if(separatorIndex >= 0)
|
||||
{
|
||||
profileMenu.removeItem(profileMenu.items[separatorIndex])
|
||||
separatorIndex = -1;
|
||||
}
|
||||
profileMenu.removeItem(object.item);
|
||||
}
|
||||
}
|
||||
|
||||
ExclusiveGroup { id: profileMenuGroup; }
|
||||
|
||||
Component
|
||||
{
|
||||
id: profileMenuItemDelegate
|
||||
MenuItem
|
||||
{
|
||||
id: item
|
||||
text: model_data ? model_data.name : ""
|
||||
checkable: true
|
||||
checked: model_data != null ? Cura.MachineManager.activeQualityId == model_data.id : false
|
||||
exclusiveGroup: profileMenuGroup
|
||||
onTriggered: Cura.MachineManager.setActiveQuality(model_data.id)
|
||||
}
|
||||
}
|
||||
|
||||
MenuSeparator { id: profileMenuSeparator }
|
||||
|
||||
MenuItem { action: Cura.Actions.addProfile }
|
||||
MenuItem { action: Cura.Actions.updateProfile }
|
||||
MenuItem { action: Cura.Actions.resetProfile }
|
||||
MenuSeparator { }
|
||||
MenuItem { action: Cura.Actions.manageProfiles }
|
||||
|
||||
MenuItem { action: Cura.Actions.configureSettingVisibility }
|
||||
}
|
||||
|
||||
Menu
|
||||
{
|
||||
id: extension_menu
|
||||
//: Extensions menu
|
||||
title: catalog.i18nc("@title:menu menubar:toplevel","E&xtensions");
|
||||
|
||||
Instantiator
|
||||
|
@ -346,8 +191,7 @@ UM.MainWindow
|
|||
|
||||
Menu
|
||||
{
|
||||
//: Settings menu
|
||||
title: catalog.i18nc("@title:menu menubar:toplevel","&Settings");
|
||||
title: catalog.i18nc("@title:menu menubar:toplevel","P&references");
|
||||
|
||||
MenuItem { action: Cura.Actions.preferences; }
|
||||
}
|
||||
|
@ -365,6 +209,16 @@ UM.MainWindow
|
|||
}
|
||||
}
|
||||
|
||||
UM.SettingPropertyProvider
|
||||
{
|
||||
id: machineExtruderCount
|
||||
|
||||
containerStackId: Cura.MachineManager.activeMachineId
|
||||
key: "machine_extruder_count"
|
||||
watchedProperties: [ "value" ]
|
||||
storeIndex: 0
|
||||
}
|
||||
|
||||
Item
|
||||
{
|
||||
id: contentItem;
|
||||
|
@ -407,29 +261,13 @@ UM.MainWindow
|
|||
}
|
||||
}
|
||||
|
||||
UM.MessageStack
|
||||
{
|
||||
anchors
|
||||
{
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
horizontalCenterOffset: -(UM.Theme.getSize("sidebar").width/ 2)
|
||||
top: parent.verticalCenter;
|
||||
bottom: parent.bottom;
|
||||
}
|
||||
}
|
||||
|
||||
Loader
|
||||
{
|
||||
id: view_panel
|
||||
|
||||
//anchors.left: parent.left;
|
||||
//anchors.right: parent.right;
|
||||
//anchors.bottom: parent.bottom
|
||||
anchors.top: viewModeButton.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height;
|
||||
anchors.left: viewModeButton.left;
|
||||
//anchors.bottom: buttons.top;
|
||||
//anchors.bottomMargin: UM.Theme.getSize("default_margin").height;
|
||||
|
||||
height: childrenRect.height;
|
||||
|
||||
|
@ -439,7 +277,6 @@ UM.MainWindow
|
|||
Button
|
||||
{
|
||||
id: openFileButton;
|
||||
//style: UM.Backend.progress < 0 ? UM.Theme.styles.open_file_button : UM.Theme.styles.tool_button;
|
||||
text: catalog.i18nc("@action:button","Open File");
|
||||
iconSource: UM.Theme.getIcon("load")
|
||||
style: UM.Theme.styles.tool_button
|
||||
|
@ -447,9 +284,7 @@ UM.MainWindow
|
|||
anchors
|
||||
{
|
||||
top: parent.top;
|
||||
//topMargin: UM.Theme.getSize("loadfile_margin").height
|
||||
left: parent.left;
|
||||
//leftMargin: UM.Theme.getSize("loadfile_margin").width
|
||||
}
|
||||
action: Cura.Actions.open;
|
||||
}
|
||||
|
@ -489,27 +324,7 @@ UM.MainWindow
|
|||
|
||||
style: UM.Theme.styles.tool_button;
|
||||
tooltip: '';
|
||||
menu: Menu
|
||||
{
|
||||
id: viewMenu;
|
||||
Instantiator
|
||||
{
|
||||
id: viewMenuInstantiator
|
||||
model: UM.ViewModel { }
|
||||
MenuItem
|
||||
{
|
||||
text: model.name
|
||||
checkable: true;
|
||||
checked: model.active
|
||||
exclusiveGroup: viewMenuGroup;
|
||||
onTriggered: UM.Controller.setActiveView(model.id);
|
||||
}
|
||||
onObjectAdded: viewMenu.insertItem(index, object)
|
||||
onObjectRemoved: viewMenu.removeItem(object)
|
||||
}
|
||||
|
||||
ExclusiveGroup { id: viewMenuGroup; }
|
||||
}
|
||||
menu: ViewMenu { }
|
||||
}
|
||||
|
||||
Toolbar
|
||||
|
@ -576,6 +391,17 @@ UM.MainWindow
|
|||
visible: base.monitoringPrint
|
||||
source: Cura.MachineManager.printerOutputDevices.length > 0 ? Cura.MachineManager.printerOutputDevices[0].cameraImage : ""
|
||||
}
|
||||
|
||||
UM.MessageStack
|
||||
{
|
||||
anchors
|
||||
{
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
horizontalCenterOffset: -(UM.Theme.getSize("sidebar").width/ 2)
|
||||
top: parent.verticalCenter;
|
||||
bottom: parent.bottom;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -650,6 +476,16 @@ UM.MainWindow
|
|||
}
|
||||
}
|
||||
|
||||
Connections
|
||||
{
|
||||
target: Cura.Actions.manageMaterials
|
||||
onTriggered:
|
||||
{
|
||||
preferences.visible = true;
|
||||
preferences.setPage(3)
|
||||
}
|
||||
}
|
||||
|
||||
Connections
|
||||
{
|
||||
target: Cura.Actions.configureSettingVisibility
|
||||
|
@ -847,6 +683,35 @@ UM.MainWindow
|
|||
}
|
||||
}
|
||||
|
||||
MessageDialog
|
||||
{
|
||||
id: messageDialog
|
||||
modality: Qt.ApplicationModal
|
||||
onAccepted: Printer.messageBoxClosed(clickedButton)
|
||||
onApply: Printer.messageBoxClosed(clickedButton)
|
||||
onDiscard: Printer.messageBoxClosed(clickedButton)
|
||||
onHelp: Printer.messageBoxClosed(clickedButton)
|
||||
onNo: Printer.messageBoxClosed(clickedButton)
|
||||
onRejected: Printer.messageBoxClosed(clickedButton)
|
||||
onReset: Printer.messageBoxClosed(clickedButton)
|
||||
onYes: Printer.messageBoxClosed(clickedButton)
|
||||
}
|
||||
|
||||
Connections
|
||||
{
|
||||
target: Printer
|
||||
onShowMessageBox:
|
||||
{
|
||||
messageDialog.title = title
|
||||
messageDialog.text = text
|
||||
messageDialog.informativeText = informativeText
|
||||
messageDialog.detailedText = detailedText
|
||||
messageDialog.standardButtons = buttons
|
||||
messageDialog.icon = icon
|
||||
messageDialog.visible = true
|
||||
}
|
||||
}
|
||||
|
||||
Connections
|
||||
{
|
||||
target: Cura.Actions.addMachine
|
||||
|
|
|
@ -24,7 +24,7 @@ Rectangle {
|
|||
UM.I18nCatalog { id: catalog; name:"cura"}
|
||||
|
||||
property variant printDuration: PrintInformation.currentPrintTime
|
||||
property real printMaterialAmount: PrintInformation.materialAmount
|
||||
property variant printMaterialAmounts: PrintInformation.materialAmounts
|
||||
|
||||
height: childrenRect.height
|
||||
color: "transparent"
|
||||
|
@ -192,7 +192,18 @@ Rectangle {
|
|||
anchors.verticalCenter: parent.verticalCenter
|
||||
font: UM.Theme.getFont("small")
|
||||
color: UM.Theme.getColor("text_subtext")
|
||||
text: catalog.i18nc("@label", "%1 m").arg(base.printMaterialAmount > 0 ? base.printMaterialAmount : 0)
|
||||
text:
|
||||
{
|
||||
var amounts = [];
|
||||
if(base.printMaterialAmounts) {
|
||||
for(var index = 0; index < base.printMaterialAmounts.length; index++) {
|
||||
amounts.push(base.printMaterialAmounts[index].toFixed(2));
|
||||
}
|
||||
} else {
|
||||
amounts = ["0.00"];
|
||||
}
|
||||
return catalog.i18nc("@label", "%1 m").arg(amounts.join(" + "));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
72
resources/qml/Menus/MaterialMenu.qml
Normal file
72
resources/qml/Menus/MaterialMenu.qml
Normal file
|
@ -0,0 +1,72 @@
|
|||
// Copyright (c) 2016 Ultimaker B.V.
|
||||
// Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.2
|
||||
import QtQuick.Controls 1.1
|
||||
|
||||
import UM 1.2 as UM
|
||||
import Cura 1.0 as Cura
|
||||
|
||||
Menu
|
||||
{
|
||||
id: menu
|
||||
title: "Material"
|
||||
|
||||
Instantiator
|
||||
{
|
||||
model: UM.InstanceContainersModel
|
||||
{
|
||||
filter:
|
||||
{
|
||||
var result = { "type": "material" }
|
||||
if(Cura.MachineManager.filterMaterialsByMachine)
|
||||
{
|
||||
result.definition = Cura.MachineManager.activeDefinitionId
|
||||
if(Cura.MachineManager.hasVariants)
|
||||
{
|
||||
result.variant = Cura.MachineManager.activeVariantId
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result.definition = "fdmprinter"
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
MenuItem
|
||||
{
|
||||
text:
|
||||
{
|
||||
var result = model.name
|
||||
|
||||
if(model.metadata.brand != undefined && model.metadata.brand != "Generic")
|
||||
{
|
||||
result = model.metadata.brand + " " + result
|
||||
}
|
||||
|
||||
if(model.metadata.color_name != undefined && model.metadata.color_name != "Generic")
|
||||
{
|
||||
result = result + " (%1)".arg(model.metadata.color_name)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
checkable: true;
|
||||
checked: model.id == Cura.MachineManager.activeMaterialId;
|
||||
exclusiveGroup: group;
|
||||
onTriggered:
|
||||
{
|
||||
Cura.MachineManager.setActiveMaterial(model.id);
|
||||
}
|
||||
}
|
||||
onObjectAdded: menu.insertItem(index, object)
|
||||
onObjectRemoved: menu.removeItem(object)
|
||||
}
|
||||
|
||||
ExclusiveGroup { id: group }
|
||||
|
||||
MenuSeparator { }
|
||||
|
||||
MenuItem { action: Cura.Actions.manageMaterials }
|
||||
}
|
37
resources/qml/Menus/NozzleMenu.qml
Normal file
37
resources/qml/Menus/NozzleMenu.qml
Normal file
|
@ -0,0 +1,37 @@
|
|||
// Copyright (c) 2016 Ultimaker B.V.
|
||||
// Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.2
|
||||
import QtQuick.Controls 1.1
|
||||
|
||||
import UM 1.2 as UM
|
||||
import Cura 1.0 as Cura
|
||||
|
||||
Menu
|
||||
{
|
||||
id: menu
|
||||
title: "Nozzle"
|
||||
|
||||
Instantiator
|
||||
{
|
||||
model: UM.InstanceContainersModel
|
||||
{
|
||||
filter:
|
||||
{
|
||||
"type": "variant",
|
||||
"definition": Cura.MachineManager.activeDefinitionId //Only show variants of this machine
|
||||
}
|
||||
}
|
||||
MenuItem {
|
||||
text: model.name;
|
||||
checkable: true;
|
||||
checked: model.id == Cura.MachineManager.activeVariantId;
|
||||
exclusiveGroup: group
|
||||
onTriggered: Cura.MachineManager.setActiveVariant(model.id)
|
||||
}
|
||||
onObjectAdded: menu.insertItem(index, object)
|
||||
onObjectRemoved: menu.removeItem(object)
|
||||
}
|
||||
|
||||
ExclusiveGroup { id: group }
|
||||
}
|
38
resources/qml/Menus/PrinterMenu.qml
Normal file
38
resources/qml/Menus/PrinterMenu.qml
Normal file
|
@ -0,0 +1,38 @@
|
|||
// Copyright (c) 2016 Ultimaker B.V.
|
||||
// Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.2
|
||||
import QtQuick.Controls 1.1
|
||||
|
||||
import UM 1.2 as UM
|
||||
import Cura 1.0 as Cura
|
||||
|
||||
Menu
|
||||
{
|
||||
id: menu;
|
||||
|
||||
Instantiator
|
||||
{
|
||||
model: UM.ContainerStacksModel
|
||||
{
|
||||
filter: {"type": "machine"}
|
||||
}
|
||||
MenuItem
|
||||
{
|
||||
text: model.name;
|
||||
checkable: true;
|
||||
checked: Cura.MachineManager.activeMachineId == model.id
|
||||
exclusiveGroup: group;
|
||||
onTriggered: Cura.MachineManager.setActiveMachine(model.id);
|
||||
}
|
||||
onObjectAdded: menu.insertItem(index, object)
|
||||
onObjectRemoved: menu.removeItem(object)
|
||||
}
|
||||
|
||||
ExclusiveGroup { id: group; }
|
||||
|
||||
MenuSeparator { }
|
||||
|
||||
MenuItem { action: Cura.Actions.addMachine; }
|
||||
MenuItem { action: Cura.Actions.configureMachines; }
|
||||
}
|
95
resources/qml/Menus/ProfileMenu.qml
Normal file
95
resources/qml/Menus/ProfileMenu.qml
Normal file
|
@ -0,0 +1,95 @@
|
|||
// Copyright (c) 2016 Ultimaker B.V.
|
||||
// Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.2
|
||||
import QtQuick.Controls 1.1
|
||||
|
||||
import UM 1.2 as UM
|
||||
import Cura 1.0 as Cura
|
||||
|
||||
Menu
|
||||
{
|
||||
id: menu
|
||||
|
||||
Instantiator
|
||||
{
|
||||
model: UM.InstanceContainersModel
|
||||
{
|
||||
filter: menu.getFilter({ "read_only": true });
|
||||
}
|
||||
|
||||
MenuItem
|
||||
{
|
||||
text: model.name
|
||||
checkable: true
|
||||
checked: Cura.MachineManager.activeQualityId == model.id
|
||||
exclusiveGroup: group
|
||||
onTriggered: Cura.MachineManager.setActiveQuality(model.id)
|
||||
}
|
||||
|
||||
onObjectAdded: menu.insertItem(index, object);
|
||||
onObjectRemoved: menu.removeItem(object);
|
||||
}
|
||||
|
||||
MenuSeparator { id: customSeparator }
|
||||
|
||||
Instantiator
|
||||
{
|
||||
id: customProfileInstantiator
|
||||
model: UM.InstanceContainersModel
|
||||
{
|
||||
filter: menu.getFilter({ "read_only": false });
|
||||
onModelReset: customSeparator.visible = rowCount() > 0
|
||||
}
|
||||
|
||||
MenuItem
|
||||
{
|
||||
text: model.name
|
||||
checkable: true
|
||||
checked: Cura.MachineManager.activeQualityId == model.id
|
||||
exclusiveGroup: group
|
||||
onTriggered: Cura.MachineManager.setActiveQuality(model.id)
|
||||
}
|
||||
|
||||
onObjectAdded:
|
||||
{
|
||||
customSeparator.visible = model.rowCount() > 0;
|
||||
menu.insertItem(index, object);
|
||||
}
|
||||
onObjectRemoved:
|
||||
{
|
||||
customSeparator.visible = model.rowCount() > 0;
|
||||
menu.removeItem(object);
|
||||
}
|
||||
}
|
||||
|
||||
ExclusiveGroup { id: group; }
|
||||
|
||||
MenuSeparator { id: profileMenuSeparator }
|
||||
|
||||
MenuItem { action: Cura.Actions.addProfile }
|
||||
MenuItem { action: Cura.Actions.updateProfile }
|
||||
MenuItem { action: Cura.Actions.resetProfile }
|
||||
MenuSeparator { }
|
||||
MenuItem { action: Cura.Actions.manageProfiles }
|
||||
|
||||
function getFilter(initial_conditions)
|
||||
{
|
||||
var result = initial_conditions;
|
||||
result.type = "quality"
|
||||
|
||||
if(Cura.MachineManager.filterQualityByMachine)
|
||||
{
|
||||
result.definition = Cura.MachineManager.activeDefinitionId;
|
||||
if(Cura.MachineManager.hasMaterials)
|
||||
{
|
||||
result.material = Cura.MachineManager.activeMaterialId;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result.definition = "fdmprinter"
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
37
resources/qml/Menus/RecentFilesMenu.qml
Normal file
37
resources/qml/Menus/RecentFilesMenu.qml
Normal file
|
@ -0,0 +1,37 @@
|
|||
// Copyright (c) 2016 Ultimaker B.V.
|
||||
// Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.2
|
||||
import QtQuick.Controls 1.1
|
||||
|
||||
import UM 1.2 as UM
|
||||
import Cura 1.0 as Cura
|
||||
|
||||
Menu
|
||||
{
|
||||
id: menu
|
||||
title: catalog.i18nc("@title:menu menubar:file", "Open &Recent")
|
||||
iconName: "document-open-recent";
|
||||
|
||||
enabled: Printer.recentFiles.length > 0;
|
||||
|
||||
Instantiator
|
||||
{
|
||||
model: Printer.recentFiles
|
||||
MenuItem
|
||||
{
|
||||
text:
|
||||
{
|
||||
var path = modelData.toString()
|
||||
return (index + 1) + ". " + path.slice(path.lastIndexOf("/") + 1);
|
||||
}
|
||||
onTriggered: {
|
||||
UM.MeshFileHandler.readLocalFile(modelData);
|
||||
var meshName = backgroundItem.getMeshName(modelData.toString())
|
||||
backgroundItem.hasMesh(decodeURIComponent(meshName))
|
||||
}
|
||||
}
|
||||
onObjectAdded: menu.insertItem(index, object)
|
||||
onObjectRemoved: menu.removeItem(object)
|
||||
}
|
||||
}
|
29
resources/qml/Menus/ViewMenu.qml
Normal file
29
resources/qml/Menus/ViewMenu.qml
Normal file
|
@ -0,0 +1,29 @@
|
|||
// Copyright (c) 2016 Ultimaker B.V.
|
||||
// Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.2
|
||||
import QtQuick.Controls 1.1
|
||||
|
||||
import UM 1.2 as UM
|
||||
import Cura 1.0 as Cura
|
||||
|
||||
Menu
|
||||
{
|
||||
title: catalog.i18nc("@title:menu menubar:toplevel", "&View");
|
||||
id: menu
|
||||
Instantiator
|
||||
{
|
||||
model: UM.ViewModel { }
|
||||
MenuItem
|
||||
{
|
||||
text: model.name;
|
||||
checkable: true;
|
||||
checked: model.active;
|
||||
exclusiveGroup: group;
|
||||
onTriggered: UM.Controller.setActiveView(model.id);
|
||||
}
|
||||
onObjectAdded: menu.insertItem(index, object)
|
||||
onObjectRemoved: menu.removeItem(object)
|
||||
}
|
||||
ExclusiveGroup { id: group; }
|
||||
}
|
|
@ -18,6 +18,22 @@ Rectangle
|
|||
property real progress: printerConnected ? Cura.MachineManager.printerOutputDevices[0].progress : 0;
|
||||
property int backendState: UM.Backend.state;
|
||||
|
||||
property variant statusColor:
|
||||
{
|
||||
if(!printerConnected)
|
||||
return UM.Theme.getColor("status_offline")
|
||||
else if(Cura.MachineManager.printerOutputDevices[0].jobState == "printing" || Cura.MachineManager.printerOutputDevices[0].jobState == "pre_print")
|
||||
return UM.Theme.getColor("status_busy")
|
||||
else if(Cura.MachineManager.printerOutputDevices[0].jobState == "ready" || Cura.MachineManager.printerOutputDevices[0].jobState == "")
|
||||
return UM.Theme.getColor("status_ready")
|
||||
else if(Cura.MachineManager.printerOutputDevices[0].jobState == "paused")
|
||||
return UM.Theme.getColor("status_paused")
|
||||
else if (Cura.MachineManager.printerOutputDevices[0].jobState == "error")
|
||||
return UM.Theme.getColor("status_stopped")
|
||||
else
|
||||
return UM.Theme.getColor("text")
|
||||
}
|
||||
|
||||
property bool activity: Printer.getPlatformActivity;
|
||||
property int totalHeight: childrenRect.height + UM.Theme.getSize("default_margin").height
|
||||
property string fileBaseName
|
||||
|
@ -25,17 +41,17 @@ Rectangle
|
|||
{
|
||||
if(!printerConnected)
|
||||
{
|
||||
return "Please check your printer connections"
|
||||
return catalog.i18nc("@label:", "Please check your printer connections")
|
||||
} else if(Cura.MachineManager.printerOutputDevices[0].jobState == "printing")
|
||||
{
|
||||
return "Printing..."
|
||||
return catalog.i18nc("@label:", "Printing...")
|
||||
} else if(Cura.MachineManager.printerOutputDevices[0].jobState == "paused")
|
||||
{
|
||||
return "Paused"
|
||||
return catalog.i18nc("@label:", "Paused")
|
||||
}
|
||||
else if(Cura.MachineManager.printerOutputDevices[0].jobState == "pre_print")
|
||||
{
|
||||
return "Preparing..."
|
||||
return catalog.i18nc("@label:", "Preparing...")
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -52,7 +68,7 @@ Rectangle
|
|||
anchors.left: parent.left
|
||||
anchors.leftMargin: UM.Theme.getSize("default_margin").width
|
||||
|
||||
color: printerConnected ? Cura.MachineManager.printerOutputDevices[0].jobState == "paused" ? UM.Theme.getColor("status_paused") : UM.Theme.getColor("status_ready") : UM.Theme.getColor("status_offline")
|
||||
color: base.statusColor
|
||||
font: UM.Theme.getFont("large")
|
||||
text: statusText;
|
||||
}
|
||||
|
@ -63,9 +79,9 @@ Rectangle
|
|||
anchors.top: parent.top
|
||||
anchors.right: progressBar.right
|
||||
|
||||
color: printerConnected ? Cura.MachineManager.printerOutputDevices[0].jobState == "paused" ? UM.Theme.getColor("status_paused") : UM.Theme.getColor("status_ready") : UM.Theme.getColor("status_offline")
|
||||
color: base.statusColor
|
||||
font: UM.Theme.getFont("large")
|
||||
text: Math.round(progress * 100) + "%";
|
||||
text: Math.round(progress) + "%";
|
||||
visible: printerConnected
|
||||
}
|
||||
|
||||
|
@ -83,9 +99,9 @@ Rectangle
|
|||
|
||||
Rectangle
|
||||
{
|
||||
width: Math.max(parent.width * base.progress)
|
||||
width: Math.max(parent.width * base.progress / 100)
|
||||
height: parent.height
|
||||
color: printerConnected ? Cura.MachineManager.printerOutputDevices[0].jobState == "paused" ? UM.Theme.getColor("status_paused") : UM.Theme.getColor("status_ready") : UM.Theme.getColor("status_offline")
|
||||
color: base.statusColor
|
||||
radius: UM.Theme.getSize("progressbar_radius").width
|
||||
}
|
||||
}
|
||||
|
@ -95,8 +111,9 @@ Rectangle
|
|||
id: abortButton
|
||||
|
||||
visible: printerConnected
|
||||
height: UM.Theme.getSize("save_button_save_to_button").height
|
||||
enabled: printerConnected && (Cura.MachineManager.printerOutputDevices[0].jobState == "paused" || Cura.MachineManager.printerOutputDevices[0].jobState == "printing")
|
||||
|
||||
height: UM.Theme.getSize("save_button_save_to_button").height
|
||||
anchors.top: progressBar.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
anchors.right: parent.right
|
||||
|
@ -140,9 +157,10 @@ Rectangle
|
|||
{
|
||||
id: pauseButton
|
||||
|
||||
height: UM.Theme.getSize("save_button_save_to_button").height
|
||||
visible: printerConnected
|
||||
enabled: printerConnected && (Cura.MachineManager.printerOutputDevices[0].jobState == "paused" || Cura.MachineManager.printerOutputDevices[0].jobState == "printing")
|
||||
|
||||
height: UM.Theme.getSize("save_button_save_to_button").height
|
||||
anchors.top: progressBar.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
anchors.right: abortButton.left
|
||||
|
|
|
@ -246,7 +246,7 @@ UM.PreferencesPage
|
|||
UM.TooltipArea {
|
||||
width: childrenRect.width
|
||||
height: childrenRect.height
|
||||
text: catalog.i18nc("@info:tooltip","Should objects be scaled up if they are extremely small?")
|
||||
text: catalog.i18nc("@info:tooltip","An object may appear extremely small if its unit is for example in meters rather than millimeters. Should these objects be scaled up?")
|
||||
|
||||
CheckBox
|
||||
{
|
||||
|
|
|
@ -27,14 +27,35 @@ UM.ManagementPage
|
|||
return -1;
|
||||
}
|
||||
|
||||
onAddObject: Printer.requestAddPrinter()
|
||||
onRemoveObject: confirmDialog.open();
|
||||
onRenameObject: renameDialog.open();
|
||||
onActivateObject: Cura.MachineManager.setActiveMachine(base.currentItem.id)
|
||||
|
||||
removeEnabled: base.currentItem != null && model.rowCount() > 1
|
||||
renameEnabled: base.currentItem != null
|
||||
activateEnabled: base.currentItem != null && base.currentItem.id != Cura.MachineManager.activeMachineId
|
||||
buttons: [
|
||||
Button
|
||||
{
|
||||
text: catalog.i18nc("@action:button", "Activate");
|
||||
iconName: "list-activate";
|
||||
enabled: base.currentItem != null && base.currentItem.id != Cura.MachineManager.activeMaterialId
|
||||
onClicked: Cura.MachineManager.setActiveMachine(base.currentItem.id)
|
||||
},
|
||||
Button
|
||||
{
|
||||
text: catalog.i18nc("@action:button", "Add");
|
||||
iconName: "list-add";
|
||||
onClicked: Printer.requestAddPrinter()
|
||||
},
|
||||
Button
|
||||
{
|
||||
text: catalog.i18nc("@action:button", "Remove");
|
||||
iconName: "list-remove";
|
||||
enabled: base.currentItem != null && model.rowCount() > 1
|
||||
onClicked: confirmDialog.open();
|
||||
},
|
||||
Button
|
||||
{
|
||||
text: catalog.i18nc("@action:button", "Rename");
|
||||
iconName: "edit-rename";
|
||||
enabled: base.currentItem != null
|
||||
onClicked: renameDialog.open();
|
||||
}
|
||||
]
|
||||
|
||||
Item
|
||||
{
|
||||
|
@ -50,10 +71,11 @@ UM.ManagementPage
|
|||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
Row
|
||||
Flow
|
||||
{
|
||||
id: machineActions
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: machineName.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
|
||||
|
|
260
resources/qml/Preferences/MaterialView.qml
Normal file
260
resources/qml/Preferences/MaterialView.qml
Normal file
|
@ -0,0 +1,260 @@
|
|||
// Copyright (c) 2016 Ultimaker B.V.
|
||||
// Uranium is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.1
|
||||
import QtQuick.Controls 1.3
|
||||
import QtQuick.Dialogs 1.2
|
||||
|
||||
import UM 1.2 as UM
|
||||
import Cura 1.0 as Cura
|
||||
|
||||
TabView
|
||||
{
|
||||
id: base
|
||||
|
||||
property QtObject properties;
|
||||
|
||||
property bool editingEnabled: false;
|
||||
property string currency: UM.Preferences.getValue("general/currency") ? UM.Preferences.getValue("general/currency") : "€"
|
||||
property real firstColumnWidth: width * 0.45
|
||||
property real secondColumnWidth: width * 0.45
|
||||
property string containerId: ""
|
||||
|
||||
Tab
|
||||
{
|
||||
title: "Information"
|
||||
anchors
|
||||
{
|
||||
leftMargin: UM.Theme.getSize("default_margin").width
|
||||
topMargin: UM.Theme.getSize("default_margin").height
|
||||
bottomMargin: UM.Theme.getSize("default_margin").height
|
||||
rightMargin: 0
|
||||
}
|
||||
|
||||
ScrollView
|
||||
{
|
||||
anchors.fill: parent
|
||||
horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff
|
||||
|
||||
Flow
|
||||
{
|
||||
id: containerGrid
|
||||
|
||||
width: base.width;
|
||||
|
||||
property real rowHeight: textField.height;
|
||||
|
||||
Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Brand") }
|
||||
ReadOnlyTextField
|
||||
{
|
||||
id: textField;
|
||||
width: base.secondColumnWidth;
|
||||
text: properties.supplier;
|
||||
readOnly: !base.editingEnabled;
|
||||
onEditingFinished: base.setMetaDataEntry("brand", properties.supplier, text)
|
||||
}
|
||||
|
||||
Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Material Type") }
|
||||
ReadOnlyTextField
|
||||
{
|
||||
width: base.secondColumnWidth;
|
||||
text: properties.material_type;
|
||||
readOnly: !base.editingEnabled;
|
||||
onEditingFinished: base.setMetaDataEntry("material", properties.material_type, text)
|
||||
}
|
||||
|
||||
Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Color") }
|
||||
|
||||
Row
|
||||
{
|
||||
width: base.secondColumnWidth;
|
||||
height: parent.rowHeight;
|
||||
spacing: UM.Theme.getSize("default_margin").width/2
|
||||
|
||||
Rectangle
|
||||
{
|
||||
id: colorSelector
|
||||
color: properties.color_code
|
||||
|
||||
width: colorLabel.height * 0.75
|
||||
height: colorLabel.height * 0.75
|
||||
border.width: UM.Theme.getSize("default_lining").height
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
MouseArea { anchors.fill: parent; onClicked: colorDialog.open(); enabled: base.editingEnabled }
|
||||
}
|
||||
ReadOnlyTextField
|
||||
{
|
||||
id: colorLabel;
|
||||
text: properties.color_name;
|
||||
readOnly: !base.editingEnabled
|
||||
onEditingFinished: base.setMetaDataEntry("color_name", properties.color_name, text)
|
||||
}
|
||||
|
||||
ColorDialog { id: colorDialog; color: properties.color_code; onAccepted: base.setMetaDataEntry("color_code", properties.color_code, color) }
|
||||
}
|
||||
|
||||
Item { width: parent.width; height: UM.Theme.getSize("default_margin").height }
|
||||
|
||||
Label { width: parent.width; height: parent.rowHeight; font.bold: true; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Properties") }
|
||||
|
||||
Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Density") }
|
||||
ReadOnlySpinBox
|
||||
{
|
||||
width: base.secondColumnWidth;
|
||||
value: properties.density;
|
||||
decimals: 2
|
||||
suffix: "g/cm"
|
||||
stepSize: 0.01
|
||||
readOnly: !base.editingEnabled;
|
||||
|
||||
onEditingFinished: base.setMetaDataEntry("properties/density", properties.density, value)
|
||||
}
|
||||
|
||||
Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Diameter") }
|
||||
ReadOnlySpinBox
|
||||
{
|
||||
width: base.secondColumnWidth;
|
||||
value: properties.diameter;
|
||||
decimals: 2
|
||||
suffix: "mm³"
|
||||
stepSize: 0.01
|
||||
readOnly: !base.editingEnabled;
|
||||
|
||||
onEditingFinished: base.setMetaDataEntry("properties/diameter", properties.diameter, value)
|
||||
}
|
||||
|
||||
Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Filament Cost") }
|
||||
SpinBox
|
||||
{
|
||||
width: base.secondColumnWidth;
|
||||
value: properties.spool_cost;
|
||||
prefix: base.currency
|
||||
enabled: false
|
||||
}
|
||||
|
||||
Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Filament weight") }
|
||||
SpinBox
|
||||
{
|
||||
width: base.secondColumnWidth;
|
||||
value: properties.spool_weight;
|
||||
suffix: "g";
|
||||
stepSize: 10
|
||||
enabled: false
|
||||
}
|
||||
|
||||
Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Filament length") }
|
||||
SpinBox
|
||||
{
|
||||
width: base.secondColumnWidth;
|
||||
value: parseFloat(properties.spool_length);
|
||||
suffix: "m";
|
||||
enabled: false
|
||||
}
|
||||
|
||||
Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Cost per Meter (Approx.)") }
|
||||
SpinBox
|
||||
{
|
||||
width: base.secondColumnWidth;
|
||||
value: parseFloat(properties.cost_per_meter);
|
||||
suffix: catalog.i18nc("@label", "%1/m".arg(base.currency));
|
||||
enabled: false
|
||||
}
|
||||
|
||||
Item { width: parent.width; height: UM.Theme.getSize("default_margin").height }
|
||||
|
||||
Label { width: parent.width; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Description") }
|
||||
|
||||
ReadOnlyTextArea
|
||||
{
|
||||
text: properties.description;
|
||||
width: base.firstColumnWidth + base.secondColumnWidth
|
||||
wrapMode: Text.WordWrap
|
||||
|
||||
readOnly: !base.editingEnabled;
|
||||
|
||||
onEditingFinished: base.setMetaDataEntry("description", properties.description, text)
|
||||
}
|
||||
|
||||
Label { width: parent.width; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Adhesion Information") }
|
||||
|
||||
ReadOnlyTextArea
|
||||
{
|
||||
text: properties.adhesion_info;
|
||||
width: base.firstColumnWidth + base.secondColumnWidth
|
||||
wrapMode: Text.WordWrap
|
||||
|
||||
readOnly: !base.editingEnabled;
|
||||
|
||||
onEditingFinished: base.setMetaDataEntry("adhesion_info", properties.adhesion_info, text)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Tab
|
||||
{
|
||||
title: catalog.i18nc("@label", "Print settings")
|
||||
anchors
|
||||
{
|
||||
leftMargin: UM.Theme.getSize("default_margin").width
|
||||
topMargin: UM.Theme.getSize("default_margin").height
|
||||
bottomMargin: UM.Theme.getSize("default_margin").height
|
||||
rightMargin: 0
|
||||
}
|
||||
|
||||
ScrollView
|
||||
{
|
||||
anchors.fill: parent;
|
||||
|
||||
ListView
|
||||
{
|
||||
model: UM.SettingDefinitionsModel
|
||||
{
|
||||
containerId: Cura.MachineManager.activeDefinitionId
|
||||
visibilityHandler: Cura.MaterialSettingsVisibilityHandler { }
|
||||
expanded: ["*"]
|
||||
}
|
||||
|
||||
delegate: UM.TooltipArea
|
||||
{
|
||||
width: childrenRect.width
|
||||
height: childrenRect.height
|
||||
text: model.description
|
||||
Label
|
||||
{
|
||||
id: label
|
||||
width: base.firstColumnWidth;
|
||||
height: spinBox.height
|
||||
text: model.label
|
||||
}
|
||||
ReadOnlySpinBox
|
||||
{
|
||||
id: spinBox
|
||||
anchors.left: label.right
|
||||
value: parseFloat(provider.properties.value);
|
||||
width: base.secondColumnWidth;
|
||||
readOnly: !base.editingEnabled
|
||||
suffix: model.unit
|
||||
maximumValue: 99999
|
||||
decimals: model.unit == "mm" ? 2 : 0
|
||||
|
||||
onEditingFinished: provider.setPropertyValue("value", value)
|
||||
}
|
||||
|
||||
UM.ContainerPropertyProvider { id: provider; containerId: base.containerId; watchedProperties: [ "value" ]; key: model.key }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tiny convenience function to check if a value really changed before trying to set it.
|
||||
function setMetaDataEntry(entry_name, old_value, new_value)
|
||||
{
|
||||
if(old_value != new_value)
|
||||
{
|
||||
Cura.ContainerManager.setContainerMetaDataEntry(base.containerId, entry_name, new_value)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -33,6 +33,8 @@ UM.ManagementPage
|
|||
}
|
||||
return result
|
||||
}
|
||||
|
||||
sectionProperty: "brand"
|
||||
}
|
||||
|
||||
activeId: Cura.MachineManager.activeMaterialId
|
||||
|
@ -45,14 +47,70 @@ UM.ManagementPage
|
|||
return -1;
|
||||
}
|
||||
|
||||
addEnabled: false
|
||||
removeEnabled: false
|
||||
renameEnabled: false
|
||||
|
||||
scrollviewCaption: " "
|
||||
scrollviewCaption: "Printer: %1, Nozzle: %2".arg(Cura.MachineManager.activeMachineName).arg(Cura.MachineManager.activeVariantName)
|
||||
detailsVisible: true
|
||||
|
||||
property string currency: UM.Preferences.getValue("general/currency")
|
||||
section.property: "section"
|
||||
section.delegate: Label
|
||||
{
|
||||
text: section
|
||||
font.bold: true
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: UM.Theme.getSize("default_lining").width;
|
||||
}
|
||||
|
||||
buttons: [
|
||||
Button
|
||||
{
|
||||
text: catalog.i18nc("@action:button", "Activate");
|
||||
iconName: "list-activate";
|
||||
enabled: base.currentItem != null && base.currentItem.id != Cura.MachineManager.activeMaterialId
|
||||
onClicked: Cura.MachineManager.setActiveMaterial(base.currentItem.id)
|
||||
},
|
||||
Button
|
||||
{
|
||||
text: catalog.i18nc("@action:button", "Duplicate");
|
||||
iconName: "list-add";
|
||||
enabled: base.currentItem != null
|
||||
onClicked:
|
||||
{
|
||||
var material_id = Cura.ContainerManager.duplicateContainer(base.currentItem.id)
|
||||
if(material_id == "")
|
||||
{
|
||||
return
|
||||
}
|
||||
|
||||
if(Cura.MachineManager.filterQualityByMachine)
|
||||
{
|
||||
var quality_id = Cura.ContainerManager.duplicateContainer(Cura.MachineManager.activeQualityId)
|
||||
Cura.ContainerManager.setContainerMetaDataEntry(quality_id, "material", material_id)
|
||||
Cura.MachineManager.setActiveQuality(quality_id)
|
||||
}
|
||||
|
||||
Cura.MachineManager.setActiveMaterial(material_id)
|
||||
}
|
||||
},
|
||||
Button
|
||||
{
|
||||
text: catalog.i18nc("@action:button", "Remove");
|
||||
iconName: "list-remove";
|
||||
enabled: base.currentItem != null && !base.currentItem.readOnly
|
||||
onClicked: confirmDialog.open()
|
||||
},
|
||||
Button
|
||||
{
|
||||
text: catalog.i18nc("@action:button", "Import");
|
||||
iconName: "document-import";
|
||||
onClicked: importDialog.open();
|
||||
},
|
||||
Button
|
||||
{
|
||||
text: catalog.i18nc("@action:button", "Export")
|
||||
iconName: "document-export"
|
||||
onClicked: exportDialog.open()
|
||||
enabled: currentItem != null
|
||||
}
|
||||
]
|
||||
|
||||
Item {
|
||||
UM.I18nCatalog { id: catalog; name: "cura"; }
|
||||
|
@ -60,126 +118,42 @@ UM.ManagementPage
|
|||
visible: base.currentItem != null
|
||||
anchors.fill: parent
|
||||
|
||||
Label { id: profileName; text: materialProperties.name; font: UM.Theme.getFont("large"); width: parent.width; }
|
||||
Item
|
||||
{
|
||||
id: profileName
|
||||
|
||||
TabView {
|
||||
id: scrollView
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: profileName.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
anchors.bottom: parent.bottom
|
||||
width: parent.width;
|
||||
height: childrenRect.height
|
||||
|
||||
Tab {
|
||||
title: "Information"
|
||||
anchors.margins: UM.Theme.getSize("default_margin").height
|
||||
Label { text: materialProperties.name; font: UM.Theme.getFont("large"); }
|
||||
Button
|
||||
{
|
||||
id: editButton
|
||||
anchors.right: parent.right;
|
||||
text: catalog.i18nc("@action:button", "Edit");
|
||||
iconName: "document-edit";
|
||||
|
||||
Flow {
|
||||
id: containerGrid
|
||||
enabled: base.currentItem != null && !base.currentItem.readOnly
|
||||
|
||||
width: scrollView.width;
|
||||
property real columnWidth: width / 2
|
||||
|
||||
Label { width: parent.columnWidth; text: catalog.i18nc("@label", "Profile Type") }
|
||||
Label { width: parent.columnWidth; text: materialProperties.profile_type }
|
||||
|
||||
Label { width: parent.columnWidth; text: catalog.i18nc("@label", "Supplier") }
|
||||
Label { width: parent.columnWidth; text: materialProperties.supplier }
|
||||
|
||||
Label { width: parent.columnWidth; text: catalog.i18nc("@label", "Material Type") }
|
||||
Label { width: parent.columnWidth; text: materialProperties.material_type }
|
||||
|
||||
Label { width: parent.columnWidth; text: catalog.i18nc("@label", "Color") }
|
||||
|
||||
Row {
|
||||
width: parent.columnWidth;
|
||||
spacing: UM.Theme.getSize("default_margin").width/2
|
||||
Rectangle {
|
||||
color: materialProperties.color_code
|
||||
width: colorLabel.height
|
||||
height: colorLabel.height
|
||||
border.width: UM.Theme.getSize("default_lining").height
|
||||
}
|
||||
Label { id: colorLabel; text: materialProperties.color_name }
|
||||
}
|
||||
|
||||
Item { width: parent.width; height: UM.Theme.getSize("default_margin").height }
|
||||
|
||||
Label { width: parent.width; text: "<b>" + catalog.i18nc("@label", "Properties") + "</b>" }
|
||||
|
||||
Label { width: parent.columnWidth; text: catalog.i18nc("@label", "Density") }
|
||||
Label { width: parent.columnWidth; text: materialProperties.density }
|
||||
|
||||
Label { width: parent.columnWidth; text: catalog.i18nc("@label", "Diameter") }
|
||||
Label { width: parent.columnWidth; text: materialProperties.diameter }
|
||||
|
||||
Label {
|
||||
text: catalog.i18nc("@label", "Filament cost")
|
||||
width: parent.columnWidth;
|
||||
height: spoolCostInput.height
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
Row {
|
||||
width: parent.columnWidth;
|
||||
Label {
|
||||
text: base.currency ? base.currency + " " : " "
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
TextField {
|
||||
id: spoolCostInput
|
||||
text: materialProperties.spool_cost
|
||||
checkable: enabled
|
||||
}
|
||||
}
|
||||
|
||||
Label { width: parent.columnWidth; text: catalog.i18nc("@label", "Filament weight") }
|
||||
Label { width: parent.columnWidth; text: materialProperties.spool_weight + " " + "g" }
|
||||
MaterialView
|
||||
{
|
||||
anchors
|
||||
{
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
top: profileName.bottom
|
||||
topMargin: UM.Theme.getSize("default_margin").height
|
||||
bottom: parent.bottom
|
||||
}
|
||||
|
||||
Label { width: parent.columnWidth; text: catalog.i18nc("@label", "Filament length") }
|
||||
Label { width: parent.columnWidth; text: materialProperties.spool_length + " " + "m" }
|
||||
editingEnabled: editButton.checkable && editButton.checked;
|
||||
|
||||
Label { width: parent.columnWidth; text: catalog.i18nc("@label", "Cost per meter") }
|
||||
Label { width: parent.columnWidth; text: catalog.i18nc("@label", "approx. %1 %2/m").arg(materialProperties.cost_per_meter).arg(base.currency); }
|
||||
|
||||
Item { width: parent.width; height: UM.Theme.getSize("default_margin").height }
|
||||
|
||||
Label {
|
||||
text: materialProperties.description ? "<b>" + catalog.i18nc("@label", "Information") + "</b><br>" + materialProperties.description : "";
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
Label {
|
||||
text: materialProperties.adhesion_info ? "<b>" + catalog.i18nc("@label", "Adhesion") + "</b><br>" + materialProperties.adhesion_info : "";
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
}
|
||||
}
|
||||
Tab {
|
||||
title: catalog.i18nc("@label", "Print settings")
|
||||
anchors.margins: UM.Theme.getSize("default_margin").height
|
||||
|
||||
Grid {
|
||||
columns: 2
|
||||
spacing: UM.Theme.getSize("default_margin").width
|
||||
|
||||
Column {
|
||||
Repeater {
|
||||
model: base.currentItem ? base.currentItem.settings : null
|
||||
Label {
|
||||
text: modelData.name.toString();
|
||||
elide: Text.ElideMiddle;
|
||||
}
|
||||
}
|
||||
}
|
||||
Column {
|
||||
Repeater {
|
||||
model: base.currentItem ? base.currentItem.settings : null
|
||||
Label { text: modelData.value.toString() + " " + modelData.unit.toString(); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
properties: materialProperties
|
||||
containerId: base.currentItem != null ? base.currentItem.id : ""
|
||||
}
|
||||
|
||||
QtObject
|
||||
|
@ -194,17 +168,100 @@ UM.ManagementPage
|
|||
property string color_name: "Yellow";
|
||||
property color color_code: "yellow";
|
||||
|
||||
property string density: "Unknown";
|
||||
property string diameter: "Unknown";
|
||||
property real density: 0.0;
|
||||
property real diameter: 0.0;
|
||||
|
||||
property string spool_cost: "Unknown";
|
||||
property string spool_weight: "Unknown";
|
||||
property string spool_length: "Unknown";
|
||||
property string cost_per_meter: "Unknown";
|
||||
property real spool_cost: 0.0;
|
||||
property real spool_weight: 0.0;
|
||||
property real spool_length: 0.0;
|
||||
property real cost_per_meter: 0.0;
|
||||
|
||||
property string description: "";
|
||||
property string adhesion_info: "";
|
||||
}
|
||||
|
||||
UM.ConfirmRemoveDialog
|
||||
{
|
||||
id: confirmDialog
|
||||
object: base.currentItem != null ? base.currentItem.name : ""
|
||||
onYes:
|
||||
{
|
||||
var containers = Cura.ContainerManager.findInstanceContainers({"GUID": base.currentItem.metadata.GUID})
|
||||
for(var i in containers)
|
||||
{
|
||||
Cura.ContainerManager.removeContainer(containers[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FileDialog
|
||||
{
|
||||
id: importDialog;
|
||||
title: catalog.i18nc("@title:window", "Import Material");
|
||||
selectExisting: true;
|
||||
nameFilters: Cura.ContainerManager.getContainerNameFilters("material")
|
||||
folder: CuraApplication.getDefaultPath()
|
||||
onAccepted:
|
||||
{
|
||||
var result = Cura.ContainerManager.importContainer(fileUrl)
|
||||
|
||||
messageDialog.title = catalog.i18nc("@title:window", "Import Material")
|
||||
messageDialog.text = catalog.i18nc("@info:status", "Could not import material <filename>%1</filename>: <message>%2</message>").arg(fileUrl).arg(result.message)
|
||||
if(result.status == "success")
|
||||
{
|
||||
messageDialog.icon = StandardIcon.Information
|
||||
messageDialog.text = catalog.i18nc("@info:status", "Successfully imported material <filename>%1</filename>").arg(fileUrl)
|
||||
}
|
||||
else if(result.status == "duplicate")
|
||||
{
|
||||
messageDialog.icon = StandardIcon.Warning
|
||||
}
|
||||
else
|
||||
{
|
||||
messageDialog.icon = StandardIcon.Critical
|
||||
}
|
||||
messageDialog.open()
|
||||
}
|
||||
}
|
||||
|
||||
FileDialog
|
||||
{
|
||||
id: exportDialog;
|
||||
title: catalog.i18nc("@title:window", "Export Material");
|
||||
selectExisting: false;
|
||||
nameFilters: Cura.ContainerManager.getContainerNameFilters("material")
|
||||
folder: CuraApplication.getDefaultPath()
|
||||
onAccepted:
|
||||
{
|
||||
if(base.currentItem.metadata.base_file)
|
||||
{
|
||||
var result = Cura.ContainerManager.exportContainer(base.currentItem.metadata.base_file, selectedNameFilter, fileUrl)
|
||||
}
|
||||
else
|
||||
{
|
||||
var result = Cura.ContainerManager.exportContainer(base.currentItem.id, selectedNameFilter, fileUrl)
|
||||
}
|
||||
|
||||
messageDialog.title = catalog.i18nc("@title:window", "Export Material")
|
||||
if(result.status == "error")
|
||||
{
|
||||
messageDialog.icon = StandardIcon.Critical
|
||||
messageDialog.text = catalog.i18nc("@info:status", "Failed to export material to <filename>%1</filename>: <message>%2</message>").arg(fileUrl).arg(result.message)
|
||||
messageDialog.open()
|
||||
}
|
||||
else if(result.status == "success")
|
||||
{
|
||||
messageDialog.icon = StandardIcon.Information
|
||||
messageDialog.text = catalog.i18nc("@info:status", "Successfully exported material to <filename>%1</filename>").arg(fileUrl)
|
||||
messageDialog.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MessageDialog
|
||||
{
|
||||
id: messageDialog
|
||||
}
|
||||
}
|
||||
|
||||
onCurrentItemChanged:
|
||||
|
@ -228,13 +285,13 @@ UM.ManagementPage
|
|||
|
||||
if(currentItem.metadata.properties != undefined && currentItem.metadata.properties != null)
|
||||
{
|
||||
materialProperties.density = currentItem.metadata.properties.density ? currentItem.metadata.properties.density : "Unknown";
|
||||
materialProperties.diameter = currentItem.metadata.properties.diameter ? currentItem.metadata.properties.diameter : "Unknown";
|
||||
materialProperties.density = currentItem.metadata.properties.density ? currentItem.metadata.properties.density : 0.0;
|
||||
materialProperties.diameter = currentItem.metadata.properties.diameter ? currentItem.metadata.properties.diameter : 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
materialProperties.density = "Unknown";
|
||||
materialProperties.diameter = "Unknown";
|
||||
materialProperties.density = 0.0;
|
||||
materialProperties.diameter = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@ UM.ManagementPage
|
|||
id: base;
|
||||
|
||||
title: catalog.i18nc("@title:tab", "Profiles");
|
||||
addText: base.currentItem && (base.currentItem.id == Cura.MachineManager.activeQualityId) ? catalog.i18nc("@label", "Create") : catalog.i18nc("@label", "Duplicate")
|
||||
|
||||
model: UM.InstanceContainersModel
|
||||
{
|
||||
|
@ -60,8 +59,21 @@ UM.ManagementPage
|
|||
return -1;
|
||||
}
|
||||
|
||||
onActivateObject: Cura.MachineManager.setActiveQuality(currentItem.id)
|
||||
onAddObject: {
|
||||
buttons: [
|
||||
Button
|
||||
{
|
||||
text: catalog.i18nc("@action:button", "Activate");
|
||||
iconName: "list-activate";
|
||||
enabled: base.currentItem != null ? base.currentItem.id != Cura.MachineManager.activeQualityId : false;
|
||||
onClicked: Cura.MachineManager.setActiveQuality(base.currentItem.id)
|
||||
},
|
||||
Button
|
||||
{
|
||||
text: base.currentItem && (base.currentItem.id == Cura.MachineManager.activeQualityId) ? catalog.i18nc("@label", "Create") : catalog.i18nc("@label", "Duplicate")
|
||||
iconName: "list-add";
|
||||
|
||||
onClicked:
|
||||
{
|
||||
var selectedContainer;
|
||||
if (objectList.currentItem.id == Cura.MachineManager.activeQualityId) {
|
||||
selectedContainer = Cura.MachineManager.newQualityContainerFromQualityAndUser();
|
||||
|
@ -74,13 +86,35 @@ UM.ManagementPage
|
|||
renameDialog.open();
|
||||
renameDialog.selectText();
|
||||
}
|
||||
onRemoveObject: confirmDialog.open();
|
||||
onRenameObject: { renameDialog.removeWhenRejected = false; renameDialog.open(); renameDialog.selectText(); }
|
||||
|
||||
activateEnabled: currentItem != null ? currentItem.id != Cura.MachineManager.activeQualityId : false;
|
||||
addEnabled: currentItem != null;
|
||||
removeEnabled: currentItem != null ? !currentItem.readOnly : false;
|
||||
renameEnabled: currentItem != null ? !currentItem.readOnly : false;
|
||||
},
|
||||
Button
|
||||
{
|
||||
text: catalog.i18nc("@action:button", "Remove");
|
||||
iconName: "list-remove";
|
||||
enabled: base.currentItem != null ? !base.currentItem.readOnly : false;
|
||||
onClicked: confirmDialog.open();
|
||||
},
|
||||
Button
|
||||
{
|
||||
text: catalog.i18nc("@action:button", "Rename");
|
||||
iconName: "edit-rename";
|
||||
enabled: base.currentItem != null ? !base.currentItem.readOnly : false;
|
||||
onClicked: { renameDialog.removeWhenRejected = false; renameDialog.open(); renameDialog.selectText(); }
|
||||
},
|
||||
Button
|
||||
{
|
||||
text: catalog.i18nc("@action:button", "Import");
|
||||
iconName: "document-import";
|
||||
onClicked: importDialog.open();
|
||||
},
|
||||
Button
|
||||
{
|
||||
text: catalog.i18nc("@action:button", "Export")
|
||||
iconName: "document-export"
|
||||
onClicked: exportDialog.open()
|
||||
enabled: currentItem != null
|
||||
}
|
||||
]
|
||||
|
||||
scrollviewCaption: catalog.i18nc("@label %1 is printer name","Printer: %1").arg(Cura.MachineManager.activeMachineName)
|
||||
|
||||
|
@ -106,7 +140,7 @@ UM.ManagementPage
|
|||
|
||||
Row {
|
||||
id: currentSettingsActions
|
||||
visible: currentItem.id == Cura.MachineManager.activeQualityId
|
||||
visible: currentItem && currentItem.id == Cura.MachineManager.activeQualityId
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.top: profileName.bottom
|
||||
|
@ -139,14 +173,14 @@ UM.ManagementPage
|
|||
|
||||
Label {
|
||||
id: defaultsMessage
|
||||
visible: !currentItem.metadata.has_settings
|
||||
visible: currentItem && !currentItem.metadata.has_settings
|
||||
text: catalog.i18nc("@action:label", "This profile has no settings and uses the defaults specified by the printer.")
|
||||
wrapMode: Text.WordWrap
|
||||
width: parent.width
|
||||
}
|
||||
Label {
|
||||
id: noCurrentSettingsMessage
|
||||
visible: currentItem.id == Cura.MachineManager.activeQualityId && !Cura.MachineManager.hasUserSettings
|
||||
visible: currentItem && currentItem.id == Cura.MachineManager.activeQualityId && !Cura.MachineManager.hasUserSettings
|
||||
text: catalog.i18nc("@action:label", "Your current settings match the selected profile.")
|
||||
wrapMode: Text.WordWrap
|
||||
width: parent.width
|
||||
|
@ -163,7 +197,17 @@ UM.ManagementPage
|
|||
anchors.bottom: parent.bottom
|
||||
|
||||
ListView {
|
||||
model: Cura.ContainerSettingsModel{ containers: (currentItem.id == Cura.MachineManager.activeQualityId) ? [base.currentItem.id, Cura.MachineManager.activeUserProfileId] : [base.currentItem.id] }
|
||||
model: Cura.ContainerSettingsModel{ containers:
|
||||
{
|
||||
if (!currentItem) {
|
||||
return []
|
||||
} else if (currentItem.id == Cura.MachineManager.activeQualityId) {
|
||||
return [base.currentItem.id, Cura.MachineManager.activeUserProfileId]
|
||||
} else {
|
||||
return [base.currentItem.id]
|
||||
}
|
||||
}
|
||||
}
|
||||
delegate: Row {
|
||||
property variant setting: model
|
||||
spacing: UM.Theme.getSize("default_margin").width/2
|
||||
|
@ -187,7 +231,7 @@ UM.ManagementPage
|
|||
}
|
||||
}
|
||||
header: Row {
|
||||
visible: currentItem.id == Cura.MachineManager.activeQualityId
|
||||
visible: currentItem && currentItem.id == Cura.MachineManager.activeQualityId
|
||||
spacing: UM.Theme.getSize("default_margin").width
|
||||
Label {
|
||||
text: catalog.i18nc("@action:label", "Profile:")
|
||||
|
@ -197,7 +241,7 @@ UM.ManagementPage
|
|||
}
|
||||
Label {
|
||||
text: catalog.i18nc("@action:label", "Current:")
|
||||
visible: currentItem.id == Cura.MachineManager.activeQualityId
|
||||
visible: currentItem && currentItem.id == Cura.MachineManager.activeQualityId
|
||||
font.bold: true
|
||||
}
|
||||
}
|
||||
|
@ -211,24 +255,6 @@ UM.ManagementPage
|
|||
}
|
||||
}
|
||||
|
||||
buttons: Row {
|
||||
|
||||
Button
|
||||
{
|
||||
text: catalog.i18nc("@action:button", "Import");
|
||||
iconName: "document-import";
|
||||
onClicked: importDialog.open();
|
||||
}
|
||||
|
||||
Button
|
||||
{
|
||||
text: catalog.i18nc("@action:button", "Export")
|
||||
iconName: "document-export"
|
||||
onClicked: exportDialog.open()
|
||||
enabled: currentItem != null
|
||||
}
|
||||
}
|
||||
|
||||
Item
|
||||
{
|
||||
UM.I18nCatalog { id: catalog; name: "uranium"; }
|
||||
|
|
52
resources/qml/Preferences/ReadOnlySpinBox.qml
Normal file
52
resources/qml/Preferences/ReadOnlySpinBox.qml
Normal file
|
@ -0,0 +1,52 @@
|
|||
// Copyright (c) 2016 Ultimaker B.V.
|
||||
// Uranium is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.1
|
||||
import QtQuick.Controls 1.1
|
||||
import QtQuick.Dialogs 1.2
|
||||
|
||||
Item
|
||||
{
|
||||
id: base
|
||||
|
||||
property alias value: spinBox.value
|
||||
property alias minimumValue: spinBox.minimumValue
|
||||
property alias maximumValue: spinBox.maximumValue
|
||||
property alias stepSize: spinBox.stepSize
|
||||
property alias prefix: spinBox.prefix
|
||||
property alias suffix: spinBox.suffix
|
||||
property alias decimals: spinBox.decimals
|
||||
|
||||
signal editingFinished();
|
||||
|
||||
property bool readOnly: false
|
||||
|
||||
width: spinBox.width
|
||||
height: spinBox.height
|
||||
|
||||
SpinBox
|
||||
{
|
||||
id: spinBox
|
||||
|
||||
enabled: !base.readOnly
|
||||
opacity: base.readOnly ? 0.5 : 1.0
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
onEditingFinished: base.editingFinished()
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
visible: base.readOnly
|
||||
text: base.prefix + base.value.toFixed(spinBox.decimals) + base.suffix
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: spinBox.__style ? spinBox.__style.padding.left : 0
|
||||
|
||||
color: palette.buttonText
|
||||
}
|
||||
|
||||
SystemPalette { id: palette }
|
||||
}
|
61
resources/qml/Preferences/ReadOnlyTextArea.qml
Normal file
61
resources/qml/Preferences/ReadOnlyTextArea.qml
Normal file
|
@ -0,0 +1,61 @@
|
|||
// Copyright (c) 2016 Ultimaker B.V.
|
||||
// Uranium is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.1
|
||||
import QtQuick.Controls 1.1
|
||||
|
||||
Item
|
||||
{
|
||||
id: base
|
||||
|
||||
property alias text: textArea.text
|
||||
property alias wrapMode: textArea.wrapMode
|
||||
|
||||
signal editingFinished();
|
||||
|
||||
property bool readOnly: false
|
||||
|
||||
width: textArea.width
|
||||
height: textArea.height
|
||||
|
||||
TextArea
|
||||
{
|
||||
id: textArea
|
||||
|
||||
enabled: !base.readOnly
|
||||
opacity: base.readOnly ? 0.5 : 1.0
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
Keys.onReturnPressed:
|
||||
{
|
||||
base.editingFinished()
|
||||
}
|
||||
|
||||
Keys.onEnterPressed:
|
||||
{
|
||||
base.editingFinished()
|
||||
}
|
||||
|
||||
onActiveFocusChanged:
|
||||
{
|
||||
if(!activeFocus)
|
||||
{
|
||||
base.editingFinished()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
visible: base.readOnly
|
||||
text: textArea.text
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: textArea.__style ? textArea.__style.textMargin : 4
|
||||
|
||||
color: palette.buttonText
|
||||
}
|
||||
|
||||
SystemPalette { id: palette }
|
||||
}
|
46
resources/qml/Preferences/ReadOnlyTextField.qml
Normal file
46
resources/qml/Preferences/ReadOnlyTextField.qml
Normal file
|
@ -0,0 +1,46 @@
|
|||
// Copyright (c) 2016 Ultimaker B.V.
|
||||
// Uranium is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.1
|
||||
import QtQuick.Controls 1.1
|
||||
import QtQuick.Dialogs 1.2
|
||||
|
||||
Item
|
||||
{
|
||||
id: base
|
||||
|
||||
property alias text: textField.text
|
||||
|
||||
signal editingFinished();
|
||||
|
||||
property bool readOnly: false
|
||||
|
||||
width: textField.width
|
||||
height: textField.height
|
||||
|
||||
TextField
|
||||
{
|
||||
id: textField
|
||||
|
||||
enabled: !base.readOnly
|
||||
opacity: base.readOnly ? 0.5 : 1.0
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
onEditingFinished: base.editingFinished()
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
visible: base.readOnly
|
||||
text: textField.text
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: textField.__panel ? textField.__panel.leftMargin : 0
|
||||
|
||||
color: palette.buttonText
|
||||
}
|
||||
|
||||
SystemPalette { id: palette }
|
||||
}
|
118
resources/qml/PrintMonitor.qml
Normal file
118
resources/qml/PrintMonitor.qml
Normal file
|
@ -0,0 +1,118 @@
|
|||
// Copyright (c) 2016 Ultimaker B.V.
|
||||
// Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.2
|
||||
import QtQuick.Controls 1.1
|
||||
import QtQuick.Controls.Styles 1.1
|
||||
import QtQuick.Layouts 1.1
|
||||
|
||||
import UM 1.2 as UM
|
||||
import Cura 1.0 as Cura
|
||||
|
||||
Column
|
||||
{
|
||||
id: printMonitor
|
||||
|
||||
Loader
|
||||
{
|
||||
sourceComponent: monitorSection
|
||||
property string label: catalog.i18nc("@label", "Temperatures")
|
||||
}
|
||||
Repeater
|
||||
{
|
||||
model: machineExtruderCount.properties.value
|
||||
delegate: Loader
|
||||
{
|
||||
sourceComponent: monitorItem
|
||||
property string label: machineExtruderCount.properties.value > 1 ? catalog.i18nc("@label", "Hotend Temperature %1").arg(index + 1) : catalog.i18nc("@label", "Hotend Temperature")
|
||||
property string value: printerConnected ? Math.round(Cura.MachineManager.printerOutputDevices[0].hotendTemperatures[index]) + "°C" : ""
|
||||
}
|
||||
}
|
||||
Repeater
|
||||
{
|
||||
model: machineHeatedBed.properties.value == "True" ? 1 : 0
|
||||
delegate: Loader
|
||||
{
|
||||
sourceComponent: monitorItem
|
||||
property string label: catalog.i18nc("@label", "Bed Temperature")
|
||||
property string value: printerConnected ? Math.round(Cura.MachineManager.printerOutputDevices[0].bedTemperature) + "°C" : ""
|
||||
}
|
||||
}
|
||||
|
||||
Loader
|
||||
{
|
||||
sourceComponent: monitorSection
|
||||
property string label: catalog.i18nc("@label", "Active print")
|
||||
}
|
||||
Loader
|
||||
{
|
||||
sourceComponent: monitorItem
|
||||
property string label: catalog.i18nc("@label", "Job Name")
|
||||
property string value: printerConnected ? Cura.MachineManager.printerOutputDevices[0].jobName : ""
|
||||
}
|
||||
Loader
|
||||
{
|
||||
sourceComponent: monitorItem
|
||||
property string label: catalog.i18nc("@label", "Printing Time")
|
||||
property string value: printerConnected ? getPrettyTime(Cura.MachineManager.printerOutputDevices[0].timeTotal) : ""
|
||||
}
|
||||
Loader
|
||||
{
|
||||
sourceComponent: monitorItem
|
||||
property string label: catalog.i18nc("@label", "Estimated time left")
|
||||
property string value: printerConnected ? getPrettyTime(Cura.MachineManager.printerOutputDevices[0].timeTotal - Cura.MachineManager.printerOutputDevices[0].timeElapsed) : ""
|
||||
}
|
||||
Loader
|
||||
{
|
||||
sourceComponent: monitorItem
|
||||
property string label: catalog.i18nc("@label", "Current Layer")
|
||||
property string value: printerConnected ? "0" : ""
|
||||
}
|
||||
|
||||
Component
|
||||
{
|
||||
id: monitorItem
|
||||
|
||||
Row
|
||||
{
|
||||
height: UM.Theme.getSize("setting_control").height
|
||||
Label
|
||||
{
|
||||
text: label
|
||||
color: printerConnected ? UM.Theme.getColor("setting_control_text") : UM.Theme.getColor("setting_control_disabled_text")
|
||||
font: UM.Theme.getFont("default")
|
||||
width: base.width * 0.4
|
||||
elide: Text.ElideRight
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
Label
|
||||
{
|
||||
text: value
|
||||
color: printerConnected ? UM.Theme.getColor("setting_control_text") : UM.Theme.getColor("setting_control_disabled_text")
|
||||
font: UM.Theme.getFont("default")
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
Component
|
||||
{
|
||||
id: monitorSection
|
||||
|
||||
Rectangle
|
||||
{
|
||||
color: UM.Theme.getColor("setting_category")
|
||||
width: base.width - 2 * UM.Theme.getSize("default_margin").width
|
||||
height: UM.Theme.getSize("section").height
|
||||
|
||||
Label
|
||||
{
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: UM.Theme.getSize("default_margin").width
|
||||
text: label
|
||||
font: UM.Theme.getFont("setting_category")
|
||||
color: UM.Theme.getColor("setting_category_text")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -114,7 +114,8 @@ Item {
|
|||
elide: Text.ElideMiddle;
|
||||
|
||||
color: UM.Theme.getColor("setting_control_text");
|
||||
font: UM.Theme.getFont("default");
|
||||
// emphasize the setting if it has a value in the user or quality profile
|
||||
font: base.stackLevel != undefined && base.stackLevel <= 1 ? UM.Theme.getFont("default_italic") : UM.Theme.getFont("default")
|
||||
}
|
||||
|
||||
Row
|
||||
|
|
|
@ -15,6 +15,16 @@ Rectangle
|
|||
|
||||
property int currentModeIndex;
|
||||
property bool monitoringPrint: false
|
||||
Connections
|
||||
{
|
||||
target: Printer
|
||||
onShowPrintMonitor:
|
||||
{
|
||||
base.monitoringPrint = show;
|
||||
showSettings.checked = !show;
|
||||
showMonitor.checked = show;
|
||||
}
|
||||
}
|
||||
|
||||
// Is there an output device for this printer?
|
||||
property bool printerConnected: Cura.MachineManager.printerOutputDevices.length != 0
|
||||
|
@ -78,34 +88,39 @@ Rectangle
|
|||
anchors.right: parent.right
|
||||
Button
|
||||
{
|
||||
id: showSettings
|
||||
width: (parent.width - UM.Theme.getSize("default_margin").width) / 2
|
||||
height: UM.Theme.getSize("sidebar_header").height
|
||||
onClicked: monitoringPrint = false
|
||||
iconSource: UM.Theme.getIcon("tab_settings");
|
||||
checkable: true
|
||||
checked: true
|
||||
checked: !monitoringPrint
|
||||
exclusiveGroup: sidebarHeaderBarGroup
|
||||
|
||||
style: UM.Theme.styles.sidebar_header_tab
|
||||
}
|
||||
Button
|
||||
{
|
||||
id: showMonitor
|
||||
width: (parent.width - UM.Theme.getSize("default_margin").width) / 2
|
||||
height: UM.Theme.getSize("sidebar_header").height
|
||||
onClicked: monitoringPrint = true
|
||||
iconSource: {
|
||||
if(!printerConnected)
|
||||
{
|
||||
return UM.Theme.getIcon("tab_monitor")
|
||||
} else if(Cura.MachineManager.printerOutputDevices[0].jobState == "paused")
|
||||
{
|
||||
return UM.Theme.getIcon("tab_monitor_paused")
|
||||
} else if (Cura.MachineManager.printerOutputDevices[0].jobState != "error")
|
||||
{
|
||||
else if(Cura.MachineManager.printerOutputDevices[0].jobState == "printing" || Cura.MachineManager.printerOutputDevices[0].jobState == "pre_print")
|
||||
return UM.Theme.getIcon("tab_monitor_busy")
|
||||
else if(Cura.MachineManager.printerOutputDevices[0].jobState == "ready" || Cura.MachineManager.printerOutputDevices[0].jobState == "")
|
||||
return UM.Theme.getIcon("tab_monitor_connected")
|
||||
}
|
||||
else if(Cura.MachineManager.printerOutputDevices[0].jobState == "paused")
|
||||
return UM.Theme.getIcon("tab_monitor_paused")
|
||||
else if (Cura.MachineManager.printerOutputDevices[0].jobState == "error")
|
||||
return UM.Theme.getIcon("tab_monitor_stopped")
|
||||
else
|
||||
return UM.Theme.getIcon("tab_monitor")
|
||||
}
|
||||
checkable: true
|
||||
checked: monitoringPrint
|
||||
exclusiveGroup: sidebarHeaderBarGroup
|
||||
|
||||
style: UM.Theme.styles.sidebar_header_tab
|
||||
|
@ -277,121 +292,15 @@ Rectangle
|
|||
}
|
||||
}
|
||||
|
||||
// Item that shows the print monitor properties
|
||||
Column
|
||||
Loader
|
||||
{
|
||||
id: printMonitor
|
||||
|
||||
anchors.bottom: footerSeparator.top
|
||||
anchors.top: monitorLabel.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
anchors.left: base.left
|
||||
anchors.leftMargin: UM.Theme.getSize("default_margin").width
|
||||
anchors.right: base.right
|
||||
visible: monitoringPrint
|
||||
|
||||
Loader
|
||||
{
|
||||
sourceComponent: monitorSection
|
||||
property string label: catalog.i18nc("@label", "Temperatures")
|
||||
}
|
||||
Repeater
|
||||
{
|
||||
model: machineExtruderCount.properties.value
|
||||
delegate: Loader
|
||||
{
|
||||
sourceComponent: monitorItem
|
||||
property string label: machineExtruderCount.properties.value > 1 ? catalog.i18nc("@label", "Hotend Temperature %1").arg(index + 1) : catalog.i18nc("@label", "Hotend Temperature")
|
||||
property string value: printerConnected ? Math.round(Cura.MachineManager.printerOutputDevices[0].hotendTemperatures[index]) + "°C" : ""
|
||||
}
|
||||
}
|
||||
Repeater
|
||||
{
|
||||
model: machineHeatedBed.properties.value == "True" ? 1 : 0
|
||||
delegate: Loader
|
||||
{
|
||||
sourceComponent: monitorItem
|
||||
property string label: catalog.i18nc("@label", "Bed Temperature")
|
||||
property string value: printerConnected ? Math.round(Cura.MachineManager.printerOutputDevices[0].bedTemperature) + "°C" : ""
|
||||
}
|
||||
}
|
||||
|
||||
Loader
|
||||
{
|
||||
sourceComponent: monitorSection
|
||||
property string label: catalog.i18nc("@label", "Active print")
|
||||
}
|
||||
Loader
|
||||
{
|
||||
sourceComponent: monitorItem
|
||||
property string label: catalog.i18nc("@label", "Job Name")
|
||||
property string value: printerConnected ? Cura.MachineManager.printerOutputDevices[0].jobName : ""
|
||||
}
|
||||
Loader
|
||||
{
|
||||
sourceComponent: monitorItem
|
||||
property string label: catalog.i18nc("@label", "Printing Time")
|
||||
property string value: printerConnected ? getPrettyTime(Cura.MachineManager.printerOutputDevices[0].timeTotal) : ""
|
||||
}
|
||||
Loader
|
||||
{
|
||||
sourceComponent: monitorItem
|
||||
property string label: catalog.i18nc("@label", "Estimated time left")
|
||||
property string value: printerConnected ? getPrettyTime(Cura.MachineManager.printerOutputDevices[0].timeTotal - Cura.MachineManager.printerOutputDevices[0].timeElapsed) : ""
|
||||
}
|
||||
Loader
|
||||
{
|
||||
sourceComponent: monitorItem
|
||||
property string label: catalog.i18nc("@label", "Current Layer")
|
||||
property string value: printerConnected ? "0" : ""
|
||||
}
|
||||
|
||||
Component
|
||||
{
|
||||
id: monitorItem
|
||||
|
||||
Row
|
||||
{
|
||||
height: UM.Theme.getSize("setting_control").height
|
||||
Label
|
||||
{
|
||||
text: label
|
||||
color: printerConnected ? UM.Theme.getColor("setting_control_text") : UM.Theme.getColor("setting_control_disabled_text")
|
||||
font: UM.Theme.getFont("default")
|
||||
width: base.width * 0.4
|
||||
elide: Text.ElideRight
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
Label
|
||||
{
|
||||
text: value
|
||||
color: printerConnected ? UM.Theme.getColor("setting_control_text") : UM.Theme.getColor("setting_control_disabled_text")
|
||||
font: UM.Theme.getFont("default")
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
Component
|
||||
{
|
||||
id: monitorSection
|
||||
|
||||
Rectangle
|
||||
{
|
||||
color: UM.Theme.getColor("setting_category")
|
||||
width: base.width - 2 * UM.Theme.getSize("default_margin").width
|
||||
height: UM.Theme.getSize("section").height
|
||||
|
||||
Label
|
||||
{
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: UM.Theme.getSize("default_margin").width
|
||||
text: label
|
||||
font: UM.Theme.getFont("setting_category")
|
||||
color: UM.Theme.getColor("setting_category_text")
|
||||
}
|
||||
}
|
||||
}
|
||||
source: monitoringPrint ? "PrintMonitor.qml": "SidebarContents.qml"
|
||||
}
|
||||
|
||||
Rectangle
|
||||
|
|
43
resources/qml/SidebarContents.qml
Normal file
43
resources/qml/SidebarContents.qml
Normal file
|
@ -0,0 +1,43 @@
|
|||
// Copyright (c) 2016 Ultimaker B.V.
|
||||
// Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.2
|
||||
import QtQuick.Controls 1.1
|
||||
import QtQuick.Controls.Styles 1.1
|
||||
import QtQuick.Layouts 1.1
|
||||
|
||||
import UM 1.2 as UM
|
||||
import Cura 1.0 as Cura
|
||||
|
||||
StackView
|
||||
{
|
||||
id: sidebarContents
|
||||
|
||||
delegate: StackViewDelegate
|
||||
{
|
||||
function transitionFinished(properties)
|
||||
{
|
||||
properties.exitItem.opacity = 1
|
||||
}
|
||||
|
||||
pushTransition: StackViewTransition
|
||||
{
|
||||
PropertyAnimation
|
||||
{
|
||||
target: enterItem
|
||||
property: "opacity"
|
||||
from: 0
|
||||
to: 1
|
||||
duration: 100
|
||||
}
|
||||
PropertyAnimation
|
||||
{
|
||||
target: exitItem
|
||||
property: "opacity"
|
||||
from: 1
|
||||
to: 0
|
||||
duration: 100
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,12 +8,14 @@ import QtQuick.Controls.Styles 1.1
|
|||
import UM 1.2 as UM
|
||||
import Cura 1.0 as Cura
|
||||
|
||||
import "Menus"
|
||||
|
||||
Column
|
||||
{
|
||||
id: base;
|
||||
|
||||
property int totalHeightHeader: childrenRect.height
|
||||
property int currentExtruderIndex: -1;
|
||||
property int currentExtruderIndex:ExtruderManager.activeExtruderIndex;
|
||||
|
||||
spacing: UM.Theme.getSize("default_margin").height
|
||||
|
||||
|
@ -56,34 +58,7 @@ Column
|
|||
|
||||
width: parent.width * 0.55 + UM.Theme.getSize("default_margin").width
|
||||
|
||||
menu: Menu
|
||||
{
|
||||
id: machineSelectionMenu
|
||||
Instantiator
|
||||
{
|
||||
model: UM.ContainerStacksModel
|
||||
{
|
||||
filter: {"type": "machine"}
|
||||
}
|
||||
MenuItem
|
||||
{
|
||||
text: model.name;
|
||||
checkable: true;
|
||||
checked: Cura.MachineManager.activeMachineId == model.id
|
||||
exclusiveGroup: machineSelectionMenuGroup;
|
||||
onTriggered: Cura.MachineManager.setActiveMachine(model.id);
|
||||
}
|
||||
onObjectAdded: machineSelectionMenu.insertItem(index, object)
|
||||
onObjectRemoved: machineSelectionMenu.removeItem(object)
|
||||
}
|
||||
|
||||
ExclusiveGroup { id: machineSelectionMenuGroup; }
|
||||
|
||||
MenuSeparator { }
|
||||
|
||||
MenuItem { action: Cura.Actions.addMachine; }
|
||||
MenuItem { action: Cura.Actions.configureMachines; }
|
||||
}
|
||||
menu: PrinterMenu { }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -117,7 +92,8 @@ Column
|
|||
onGlobalContainerChanged:
|
||||
{
|
||||
base.currentExtruderIndex = -1;
|
||||
ExtruderManager.setActiveExtruderIndex(0);
|
||||
forceActiveFocus()
|
||||
ExtruderManager.setActiveExtruderIndex(base.currentExtruderIndex);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -134,7 +110,7 @@ Column
|
|||
|
||||
onClicked:
|
||||
{
|
||||
focus = true; //Changing focus applies the currently-being-typed values so it can change the displayed setting values.
|
||||
forceActiveFocus() //Changing focus applies the currently-being-typed values so it can change the displayed setting values.
|
||||
base.currentExtruderIndex = index;
|
||||
ExtruderManager.setActiveExtruderIndex(index);
|
||||
}
|
||||
|
@ -235,37 +211,7 @@ Column
|
|||
anchors.left: parent.left
|
||||
style: UM.Theme.styles.sidebar_header_button
|
||||
|
||||
menu: Menu
|
||||
{
|
||||
id: variantsSelectionMenu
|
||||
Instantiator
|
||||
{
|
||||
id: variantSelectionInstantiator
|
||||
model: UM.InstanceContainersModel
|
||||
{
|
||||
filter:
|
||||
{
|
||||
"type": "variant",
|
||||
"definition": Cura.MachineManager.activeDefinitionId //Only show variants of this machine
|
||||
}
|
||||
}
|
||||
MenuItem
|
||||
{
|
||||
text: model.name;
|
||||
checkable: true;
|
||||
checked: model.id == Cura.MachineManager.activeVariantId;
|
||||
exclusiveGroup: variantSelectionMenuGroup;
|
||||
onTriggered:
|
||||
{
|
||||
Cura.MachineManager.setActiveVariant(model.id);
|
||||
}
|
||||
}
|
||||
onObjectAdded: variantsSelectionMenu.insertItem(index, object)
|
||||
onObjectRemoved: variantsSelectionMenu.removeItem(object)
|
||||
}
|
||||
|
||||
ExclusiveGroup { id: variantSelectionMenuGroup; }
|
||||
}
|
||||
menu: NozzleMenu { }
|
||||
}
|
||||
|
||||
ToolButton {
|
||||
|
@ -280,49 +226,7 @@ Column
|
|||
anchors.right: parent.right
|
||||
style: UM.Theme.styles.sidebar_header_button
|
||||
|
||||
menu: Menu
|
||||
{
|
||||
id: materialSelectionMenu
|
||||
Instantiator
|
||||
{
|
||||
id: materialSelectionInstantiator
|
||||
model: UM.InstanceContainersModel
|
||||
{
|
||||
filter:
|
||||
{
|
||||
var result = { "type": "material" }
|
||||
if(Cura.MachineManager.filterMaterialsByMachine)
|
||||
{
|
||||
result.definition = Cura.MachineManager.activeDefinitionId
|
||||
if(Cura.MachineManager.hasVariants)
|
||||
{
|
||||
result.variant = Cura.MachineManager.activeVariantId
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result.definition = "fdmprinter"
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
MenuItem
|
||||
{
|
||||
text: model.name;
|
||||
checkable: true;
|
||||
checked: model.id == Cura.MachineManager.activeMaterialId;
|
||||
exclusiveGroup: materialSelectionMenuGroup;
|
||||
onTriggered:
|
||||
{
|
||||
Cura.MachineManager.setActiveMaterial(model.id);
|
||||
}
|
||||
}
|
||||
onObjectAdded: materialSelectionMenu.insertItem(index, object)
|
||||
onObjectRemoved: materialSelectionMenu.removeItem(object)
|
||||
}
|
||||
|
||||
ExclusiveGroup { id: materialSelectionMenuGroup; }
|
||||
}
|
||||
menu: MaterialMenu { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -359,88 +263,7 @@ Column
|
|||
tooltip: Cura.MachineManager.activeQualityName
|
||||
style: UM.Theme.styles.sidebar_header_button
|
||||
|
||||
menu: Menu
|
||||
{
|
||||
id: profileSelectionMenu
|
||||
Instantiator
|
||||
{
|
||||
id: profileSelectionInstantiator
|
||||
model: UM.InstanceContainersModel
|
||||
{
|
||||
filter:
|
||||
{
|
||||
var result = { "type": "quality" };
|
||||
if(Cura.MachineManager.filterQualityByMachine)
|
||||
{
|
||||
result.definition = Cura.MachineManager.activeDefinitionId;
|
||||
if(Cura.MachineManager.hasMaterials)
|
||||
{
|
||||
result.material = Cura.MachineManager.activeMaterialId;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result.definition = "fdmprinter"
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
property int separatorIndex: -1
|
||||
|
||||
Loader {
|
||||
property QtObject model_data: model
|
||||
property int model_index: index
|
||||
sourceComponent: menuItemDelegate
|
||||
}
|
||||
onObjectAdded:
|
||||
{
|
||||
//Insert a separator between readonly and custom profiles
|
||||
if(separatorIndex < 0 && index > 0)
|
||||
{
|
||||
if(model.getItem(index-1).readOnly != model.getItem(index).readOnly)
|
||||
{
|
||||
profileSelectionMenu.insertSeparator(index);
|
||||
separatorIndex = index;
|
||||
}
|
||||
}
|
||||
//Because of the separator, custom profiles move one index lower
|
||||
profileSelectionMenu.insertItem((model.getItem(index).readOnly) ? index : index + 1, object.item);
|
||||
}
|
||||
onObjectRemoved:
|
||||
{
|
||||
//When adding a profile, the menu is rebuilt by removing all items.
|
||||
//If a separator was added, we need to remove that too.
|
||||
if(separatorIndex >= 0)
|
||||
{
|
||||
profileSelectionMenu.removeItem(profileSelectionMenu.items[separatorIndex])
|
||||
separatorIndex = -1;
|
||||
}
|
||||
profileSelectionMenu.removeItem(object.item);
|
||||
}
|
||||
}
|
||||
ExclusiveGroup { id: profileSelectionMenuGroup; }
|
||||
|
||||
Component
|
||||
{
|
||||
id: menuItemDelegate
|
||||
MenuItem
|
||||
{
|
||||
id: item
|
||||
text: model_data ? model_data.name : ""
|
||||
checkable: true
|
||||
checked: model_data != null ? Cura.MachineManager.activeQualityId == model_data.id : false
|
||||
exclusiveGroup: profileSelectionMenuGroup;
|
||||
onTriggered: Cura.MachineManager.setActiveQuality(model_data.id)
|
||||
}
|
||||
}
|
||||
|
||||
MenuSeparator { }
|
||||
MenuItem { action: Cura.Actions.addProfile }
|
||||
MenuItem { action: Cura.Actions.updateProfile }
|
||||
MenuItem { action: Cura.Actions.resetProfile }
|
||||
MenuSeparator { }
|
||||
MenuItem { action: Cura.Actions.manageProfiles }
|
||||
}
|
||||
menu: ProfileMenu { }
|
||||
|
||||
UM.SimpleButton
|
||||
{
|
||||
|
|
|
@ -371,7 +371,7 @@ Item
|
|||
{
|
||||
id: infillDensity
|
||||
|
||||
containerStackId: Cura.MachineManager.activeMachineId
|
||||
containerStackId: Cura.MachineManager.activeStackId
|
||||
key: "infill_sparse_density"
|
||||
watchedProperties: [ "value" ]
|
||||
storeIndex: 0
|
||||
|
@ -406,6 +406,7 @@ Item
|
|||
watchedProperties: [ "value" ]
|
||||
storeIndex: 0
|
||||
}
|
||||
|
||||
UM.SettingPropertyProvider
|
||||
{
|
||||
id: supportExtruderNr
|
||||
|
|
|
@ -14,6 +14,11 @@
|
|||
"bold": true,
|
||||
"family": "Open Sans"
|
||||
},
|
||||
"default_italic": {
|
||||
"size": 1.15,
|
||||
"italic": true,
|
||||
"family": "Open Sans"
|
||||
},
|
||||
"small": {
|
||||
"size": 1.0,
|
||||
"bold": true,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue