diff --git a/cura/CameraImageProvider.py b/cura/CameraImageProvider.py
new file mode 100644
index 0000000000..ff66170f3c
--- /dev/null
+++ b/cura/CameraImageProvider.py
@@ -0,0 +1,18 @@
+from PyQt5.QtGui import QImage
+from PyQt5.QtQuick import QQuickImageProvider
+from PyQt5.QtCore import QSize
+
+from UM.Application import Application
+
+class CameraImageProvider(QQuickImageProvider):
+ def __init__(self):
+ QQuickImageProvider.__init__(self, QQuickImageProvider.Image)
+
+ ## Request a new image.
+ def requestImage(self, id, size):
+ for output_device in Application.getInstance().getOutputDeviceManager().getOutputDevices():
+ try:
+ return output_device.getCameraImage(), QSize(15, 15)
+ except AttributeError:
+ pass
+ return QImage(), QSize(15, 15)
\ No newline at end of file
diff --git a/cura/ConvexHullDecorator.py b/cura/ConvexHullDecorator.py
index 4aa6584dc5..f8d1a1e12e 100644
--- a/cura/ConvexHullDecorator.py
+++ b/cura/ConvexHullDecorator.py
@@ -144,6 +144,8 @@ class ConvexHullDecorator(SceneNodeDecorator):
else:
rounded_hull = None
+ mesh = None
+ world_transform = None
if self._node.getMeshData():
mesh = self._node.getMeshData()
world_transform = self._node.getWorldTransformation()
diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py
index 420ae276e9..3664563590 100644
--- a/cura/CuraApplication.py
+++ b/cura/CuraApplication.py
@@ -44,6 +44,7 @@ from . import ZOffsetDecorator
from . import CuraSplashScreen
from . import MachineManagerModel
from . import ContainerSettingsModel
+from . import CameraImageProvider
from . import MachineActionManager
from . import ContainerManager
@@ -124,7 +125,6 @@ class CuraApplication(QtApplication):
self._platform = None
self._output_devices = {}
self._print_information = None
- self._i18n_catalog = None
self._previous_active_tool = None
self._platform_activity = False
self._scene_bounding_box = AxisAlignedBox.Null
@@ -135,12 +135,16 @@ class CuraApplication(QtApplication):
self._cura_actions = None
self._started = False
+ self._i18n_catalog = i18nCatalog("cura")
+
self.getController().getScene().sceneChanged.connect(self.updatePlatformActivity)
self.getController().toolOperationStopped.connect(self._onToolOperationStopped)
Resources.addType(self.ResourceTypes.QmlFiles, "qml")
Resources.addType(self.ResourceTypes.Firmware, "firmware")
+ 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")
@@ -229,7 +233,7 @@ class CuraApplication(QtApplication):
JobQueue.getInstance().jobFinished.connect(self._onJobFinished)
self.applicationShuttingDown.connect(self.saveSettings)
-
+ self.engineCreatedSignal.connect(self._onEngineCreated)
self._recent_files = []
files = Preferences.getInstance().getValue("cura/recent_files").split(";")
for f in files:
@@ -238,6 +242,11 @@ class CuraApplication(QtApplication):
self._recent_files.append(QUrl.fromLocalFile(f))
+ def _onEngineCreated(self):
+ self._engine.addImageProvider("camera", CameraImageProvider.CameraImageProvider())
+
+ 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.
@@ -327,13 +336,6 @@ class CuraApplication(QtApplication):
parser.add_argument("--debug", dest="debug-mode", action="store_true", default=False, help="Enable detailed crash reports.")
def run(self):
- self._i18n_catalog = i18nCatalog("cura");
-
- i18nCatalog.setTagReplacements({
- "filename": "font color=\"black\"",
- "message": "font color=UM.Theme.colors.message_text;",
- })
-
self.showSplashMessage(self._i18n_catalog.i18nc("@info:progress", "Setting up scene..."))
controller = self.getController()
@@ -554,12 +556,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()
@@ -817,3 +819,7 @@ class CuraApplication(QtApplication):
def _addProfileWriter(self, profile_writer):
pass
+
+ @pyqtSlot("QSize")
+ def setMinimumWindowSize(self, size):
+ self.getMainWindow().setMinimumSize(size)
diff --git a/cura/CuraSplashScreen.py b/cura/CuraSplashScreen.py
index f2810d359b..1df2c39da7 100644
--- a/cura/CuraSplashScreen.py
+++ b/cura/CuraSplashScreen.py
@@ -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()
diff --git a/cura/MachineManagerModel.py b/cura/MachineManagerModel.py
index 70953ee712..76786efe61 100644
--- a/cura/MachineManagerModel.py
+++ b/cura/MachineManagerModel.py
@@ -84,8 +84,8 @@ class MachineManagerModel(QObject):
self._global_stack_valid = False
self.globalValidationChanged.emit()
else:
- new_validation_state = self._checkStackForErrors(self._active_container_stack)
- if new_validation_state:
+ has_errors = self._checkStackForErrors(self._active_container_stack)
+ if not has_errors:
self._global_stack_valid = True
self.globalValidationChanged.emit()
@@ -104,6 +104,7 @@ class MachineManagerModel(QObject):
self._global_stack_valid = not self._checkStackForErrors(self._global_container_stack)
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)
@@ -202,7 +203,7 @@ class MachineManagerModel(QObject):
@pyqtProperty(bool, notify = activeStackChanged)
def hasUserSettings(self):
if not self._active_container_stack:
- return
+ return False
user_settings = self._active_container_stack.getTop().findInstances(**{})
return len(user_settings) != 0
@@ -212,7 +213,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):
@@ -285,7 +286,7 @@ class MachineManagerModel(QObject):
self.blurSettings.emit()
self.setActiveQuality(new_container_id)
self.updateQualityContainerFromUserContainer()
-
+ return new_container_id
@pyqtSlot(str, result=str)
def duplicateContainer(self, container_id):
@@ -357,7 +358,6 @@ class MachineManagerModel(QObject):
self.setActiveQuality(containers[0].getId())
self.activeQualityChanged.emit()
-
@pyqtSlot()
def updateQualityContainerFromUserContainer(self):
if not self._active_container_stack:
@@ -401,9 +401,9 @@ class MachineManagerModel(QObject):
preferred_material = None
if old_material:
- preferred_material = old_material.getId()
+ preferred_material_name = old_material.getName()
- self.setActiveMaterial(self._updateMaterialContainer(self._global_container_stack.getBottom(), containers[0], preferred_material).id)
+ self.setActiveMaterial(self._updateMaterialContainer(self._global_container_stack.getBottom(), containers[0], preferred_material_name).id)
@pyqtSlot(str)
def setActiveQuality(self, quality_id):
@@ -496,6 +496,12 @@ class MachineManagerModel(QObject):
return False
+ @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()
+
def _updateVariantContainer(self, definition):
if not definition.getMetaDataEntry("has_variants"):
return self._empty_variant_container
@@ -513,7 +519,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
@@ -527,15 +533,26 @@ 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
+ if preferred_material:
+ search_criteria["id"] = preferred_material
containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(**search_criteria)
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):
@@ -560,6 +577,15 @@ 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):
diff --git a/cura/PlatformPhysics.py b/cura/PlatformPhysics.py
index c43d0d09d7..e4844baf31 100644
--- a/cura/PlatformPhysics.py
+++ b/cura/PlatformPhysics.py
@@ -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__()
@@ -100,18 +97,15 @@ class PlatformPhysics:
# 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"))
- if not overlap:
- other_head_hull = other_node.callDecoration("getConvexHullHead")
- 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.
+ head_hull = node.callDecoration("getConvexHullHead")
+ if head_hull:
+ overlap = head_hull.intersectsPolygon(other_node.callDecoration("getConvexHull"))
+ if not overlap:
+ other_head_hull = other_node.callDecoration("getConvexHullHead")
+ if other_head_hull:
+ overlap = node.callDecoration("getConvexHull").intersectsPolygon(other_head_hull)
+ else:
+ overlap = node.callDecoration("getConvexHull").intersectsPolygon(other_node.callDecoration("getConvexHull"))
if overlap is None:
continue
diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py
index 7649106523..7f6e51e1fd 100644
--- a/cura/PrinterOutputDevice.py
+++ b/cura/PrinterOutputDevice.py
@@ -29,6 +29,10 @@ class PrinterOutputDevice(QObject, OutputDevice):
self._head_y = 0
self._head_z = 0
self._connection_state = ConnectionState.closed
+ self._time_elapsed = 0
+ self._time_total = 0
+ self._job_state = ""
+ self._job_name = ""
def requestWrite(self, node, file_name = None, filter_by_machine = False):
raise NotImplementedError("requestWrite needs to be implemented")
@@ -57,6 +61,39 @@ class PrinterOutputDevice(QObject, OutputDevice):
# it also sends it's own device_id (for convenience sake)
connectionStateChanged = pyqtSignal(str)
+ timeElapsedChanged = pyqtSignal()
+
+ timeTotalChanged = pyqtSignal()
+
+ jobStateChanged = pyqtSignal()
+
+ jobNameChanged = pyqtSignal()
+
+ @pyqtProperty(str, notify = jobStateChanged)
+ def jobState(self):
+ return self._job_state
+
+ def _updateJobState(self, job_state):
+ if self._job_state != job_state:
+ self._job_state = job_state
+ self.jobStateChanged.emit()
+
+ @pyqtSlot(str)
+ def setJobState(self, job_state):
+ self._setJobState(job_state)
+
+ def _setJobState(self, job_state):
+ Logger.log("w", "_setJobState is not implemented by this output device")
+
+ @pyqtProperty(str, notify = jobNameChanged)
+ def jobName(self):
+ return self._job_name
+
+ def setJobName(self, name):
+ if self._job_name != name:
+ self._job_name = name
+ self.jobNameChanged.emit()
+
## Get the bed temperature of the bed (if any)
# This function is "final" (do not re-implement)
# /sa _getBedTemperature implementation function
@@ -74,6 +111,30 @@ class PrinterOutputDevice(QObject, OutputDevice):
self._target_bed_temperature = temperature
self.targetBedTemperatureChanged.emit()
+ ## Time the print has been printing.
+ # Note that timeTotal - timeElapsed should give time remaining.
+ @pyqtProperty(float, notify = timeElapsedChanged)
+ def timeElapsed(self):
+ return self._time_elapsed
+
+ ## Total time of the print
+ # Note that timeTotal - timeElapsed should give time remaining.
+ @pyqtProperty(float, notify=timeTotalChanged)
+ def timeTotal(self):
+ return self._time_total
+
+ @pyqtSlot(float)
+ def setTimeTotal(self, new_total):
+ if self._time_total != new_total:
+ self._time_total = new_total
+ self.timeTotalChanged.emit()
+
+ @pyqtSlot(float)
+ def setTimeElapsed(self, time_elapsed):
+ if self._time_elapsed != time_elapsed:
+ self._time_elapsed = time_elapsed
+ self.timeElapsedChanged.emit()
+
## Home the head of the connected printer
# This function is "final" (do not re-implement)
# /sa _homeHead implementation function
diff --git a/cura/ZOffsetDecorator.py b/cura/ZOffsetDecorator.py
index 5c3c9e219b..66dddfd390 100644
--- a/cura/ZOffsetDecorator.py
+++ b/cura/ZOffsetDecorator.py
@@ -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
diff --git a/plugins/3MFReader/ThreeMFReader.py b/plugins/3MFReader/ThreeMFReader.py
index 824eff9242..84f71cf253 100644
--- a/plugins/3MFReader/ThreeMFReader.py
+++ b/plugins/3MFReader/ThreeMFReader.py
@@ -2,7 +2,7 @@
# Cura is released under the terms of the AGPLv3 or higher.
from UM.Mesh.MeshReader import MeshReader
-from UM.Mesh.MeshData import MeshData
+from UM.Mesh.MeshBuilder import MeshBuilder
from UM.Logger import Logger
from UM.Math.Matrix import Matrix
from UM.Math.Vector import Vector
@@ -42,7 +42,7 @@ class ThreeMFReader(MeshReader):
return None
for entry in objects:
- mesh = MeshData()
+ mesh_builder = MeshBuilder()
node = SceneNode()
vertex_list = []
#for vertex in entry.mesh.vertices.vertex:
@@ -51,71 +51,67 @@ class ThreeMFReader(MeshReader):
Job.yieldThread()
triangles = entry.findall(".//3mf:triangle", self._namespaces)
-
- mesh.reserveFaceCount(len(triangles))
+ 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"))
v3 = int(triangle.get("v3"))
- mesh.addFace(vertex_list[v1][0],vertex_list[v1][1],vertex_list[v1][2],vertex_list[v2][0],vertex_list[v2][1],vertex_list[v2][2],vertex_list[v3][0],vertex_list[v3][1],vertex_list[v3][2])
+
+ mesh_builder.addFaceByPoints(vertex_list[v1][0], vertex_list[v1][1], vertex_list[v1][2],
+ vertex_list[v2][0], vertex_list[v2][1], vertex_list[v2][2],
+ vertex_list[v3][0], vertex_list[v3][1], vertex_list[v3][2])
+
Job.yieldThread()
# Rotate the model; We use a different coordinate frame.
rotation = Matrix()
rotation.setByRotationAxis(-0.5 * math.pi, Vector(1,0,0))
- mesh = mesh.getTransformed(rotation)
#TODO: We currently do not check for normals and simply recalculate them.
- mesh.calculateNormals()
- node.setMeshData(mesh)
+ mesh_builder.calculateNormals()
+
+ node.setMeshData(mesh_builder.build().getTransformed(rotation))
node.setSelectable(True)
- transformation = root.findall("./3mf:build/3mf:item[@objectid='{0}']".format(entry.get("id")), self._namespaces)
- if transformation:
- transformation = transformation[0]
- try:
- if transformation.get("transform"):
- splitted_transformation = transformation.get("transform").split()
- ## Transformation is saved as:
- ## M00 M01 M02 0.0
- ## M10 M11 M12 0.0
- ## M20 M21 M22 0.0
- ## M30 M31 M32 1.0
- ## We switch the row & cols as that is how everyone else uses matrices!
- temp_mat = Matrix()
- # Rotation & Scale
- temp_mat._data[0,0] = splitted_transformation[0]
- temp_mat._data[1,0] = splitted_transformation[1]
- temp_mat._data[2,0] = splitted_transformation[2]
- temp_mat._data[0,1] = splitted_transformation[3]
- temp_mat._data[1,1] = splitted_transformation[4]
- temp_mat._data[2,1] = splitted_transformation[5]
- temp_mat._data[0,2] = splitted_transformation[6]
- temp_mat._data[1,2] = splitted_transformation[7]
- temp_mat._data[2,2] = splitted_transformation[8]
+ transformations = root.findall("./3mf:build/3mf:item[@objectid='{0}']".format(entry.get("id")), self._namespaces)
+ transformation = transformations[0] if transformations else None
+ if transformation is not None and transformation.get("transform"):
+ splitted_transformation = transformation.get("transform").split()
+ ## Transformation is saved as:
+ ## M00 M01 M02 0.0
+ ## M10 M11 M12 0.0
+ ## M20 M21 M22 0.0
+ ## M30 M31 M32 1.0
+ ## We switch the row & cols as that is how everyone else uses matrices!
+ temp_mat = Matrix()
+ # Rotation & Scale
+ temp_mat._data[0,0] = splitted_transformation[0]
+ temp_mat._data[1,0] = splitted_transformation[1]
+ temp_mat._data[2,0] = splitted_transformation[2]
+ temp_mat._data[0,1] = splitted_transformation[3]
+ temp_mat._data[1,1] = splitted_transformation[4]
+ temp_mat._data[2,1] = splitted_transformation[5]
+ temp_mat._data[0,2] = splitted_transformation[6]
+ temp_mat._data[1,2] = splitted_transformation[7]
+ temp_mat._data[2,2] = splitted_transformation[8]
- # Translation
- temp_mat._data[0,3] = splitted_transformation[9]
- temp_mat._data[1,3] = splitted_transformation[10]
- temp_mat._data[2,3] = splitted_transformation[11]
+ # Translation
+ temp_mat._data[0,3] = splitted_transformation[9]
+ temp_mat._data[1,3] = splitted_transformation[10]
+ temp_mat._data[2,3] = splitted_transformation[11]
- node.setTransformation(temp_mat)
- except AttributeError:
- pass # Empty list was found. Getting transformation is not possible
+ node.setTransformation(temp_mat)
result.addChild(node)
Job.yieldThread()
#If there is more then one object, group them.
- try:
- if len(objects) > 1:
- group_decorator = GroupDecorator()
- result.addDecorator(group_decorator)
- except:
- pass
+ 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)
diff --git a/plugins/USBPrinting/ControlWindow.qml b/plugins/USBPrinting/ControlWindow.qml
deleted file mode 100644
index 0938e8e6d3..0000000000
--- a/plugins/USBPrinting/ControlWindow.qml
+++ /dev/null
@@ -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 with 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
- }
- ]
-}
diff --git a/plugins/USBPrinting/USBPrinterOutputDevice.py b/plugins/USBPrinting/USBPrinterOutputDevice.py
index 58b75c2987..2938604eed 100644
--- a/plugins/USBPrinting/USBPrinterOutputDevice.py
+++ b/plugins/USBPrinting/USBPrinterOutputDevice.py
@@ -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
@@ -51,13 +51,14 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
self._update_firmware_thread = threading.Thread(target= self._updateFirmware)
self._update_firmware_thread.daemon = True
self.firmwareUpdateComplete.connect(self._onFirmwareUpdateComplete)
-
+
self._heatup_wait_start_time = time.time()
## Queue for commands that need to be send. Used when command is sent when a print is active.
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
@@ -80,13 +81,15 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
# In order to keep the connection alive we request the temperature every so often from a different extruder.
# This index is the extruder we requested data from the last time.
- self._temperature_requested_extruder_index = 0
+ 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 +123,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 +138,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
@@ -344,23 +349,7 @@ 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.
+ ## Send a command to printer.
# \param cmd string with g-code
def sendCommand(self, cmd):
if self._progress:
@@ -371,11 +360,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":
@@ -391,14 +382,14 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
self.endstopStateChanged.emit("z_min", value)
self._z_min_endstop_pressed = value
- ## Listen thread function.
+ ## Listen thread function.
def _listen(self):
Logger.log("i", "Printer connection listen thread started for %s" % self._serial_port)
temperature_request_timeout = time.time()
ok_timeout = time.time()
while self._connection_state == ConnectionState.connected:
line = self._readline()
- if line is None:
+ if line is None:
break # None is only returned when something went wrong. Stop listening
if time.time() > temperature_request_timeout:
@@ -423,7 +414,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
self._setErrorState(line[6:])
elif b" T:" in line or line.startswith(b"T:"): # Temperature message
- try:
+ try:
self._setHotendTemperature(self._temperature_requested_extruder_index, float(re.search(b"T: *([0-9\.]*)", line).group(1)))
except:
pass
@@ -445,6 +436,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 +447,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.)
- 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")
+ # 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
@@ -487,10 +481,22 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
checksum = functools.reduce(lambda x,y: x^y, map(ord, "N%d%s" % (self._gcode_position, line)))
self._sendCommand("N%d%s*%d" % (self._gcode_position, line, checksum))
- self._gcode_position += 1
+ self._gcode_position += 1
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 +504,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):
diff --git a/plugins/UltimakerMachineActions/BedLevelMachineAction.py b/plugins/UltimakerMachineActions/BedLevelMachineAction.py
index b6b52c552e..7dad841340 100644
--- a/plugins/UltimakerMachineActions/BedLevelMachineAction.py
+++ b/plugins/UltimakerMachineActions/BedLevelMachineAction.py
@@ -1,14 +1,16 @@
from cura.MachineAction import MachineAction
+from cura.PrinterOutputDevice import PrinterOutputDevice
-from PyQt5.QtCore import pyqtSlot
+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
diff --git a/plugins/UltimakerMachineActions/UMOCheckupMachineAction.py b/plugins/UltimakerMachineActions/UMOCheckupMachineAction.py
index 0c8bf3480f..f13257e8a9 100644
--- a/plugins/UltimakerMachineActions/UMOCheckupMachineAction.py
+++ b/plugins/UltimakerMachineActions/UMOCheckupMachineAction.py
@@ -3,9 +3,13 @@ from cura.PrinterOutputDevice import PrinterOutputDevice
from UM.Application import Application
from PyQt5.QtCore import pyqtSlot, pyqtSignal, pyqtProperty
+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
diff --git a/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml b/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml
index 1c3ac84010..4b25d3130d 100644
--- a/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml
+++ b/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml
@@ -52,7 +52,6 @@ Cura.MachineAction
onClicked:
{
checkupContent.visible = true
- startCheckButton.enabled = false
manager.startCheck()
}
}
@@ -94,7 +93,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: Cura.USBPrinterManager.connectedPrinterList.rowCount() > 0 ? catalog.i18nc("@info:status","Connected"): catalog.i18nc("@info:status","Not connected")
}
//////////////////////////////////////////////////////////
Label
diff --git a/plugins/UltimakerMachineActions/UMOUpgradeSelection.py b/plugins/UltimakerMachineActions/UMOUpgradeSelection.py
new file mode 100644
index 0000000000..2e13ec50d5
--- /dev/null
+++ b/plugins/UltimakerMachineActions/UMOUpgradeSelection.py
@@ -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()
\ No newline at end of file
diff --git a/plugins/UltimakerMachineActions/UMOUpgradeSelectionMachineAction.qml b/plugins/UltimakerMachineActions/UMOUpgradeSelectionMachineAction.qml
new file mode 100644
index 0000000000..53a7e47a6b
--- /dev/null
+++ b/plugins/UltimakerMachineActions/UMOUpgradeSelectionMachineAction.qml
@@ -0,0 +1,39 @@
+// 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: bedLevelMachineAction
+ anchors.fill: parent;
+
+ UM.I18nCatalog { id: catalog; name: "cura"; }
+ Column
+ {
+ anchors.fill: parent;
+ Label
+ {
+ width: parent.width
+ wrapMode: Text.WordWrap
+ text: catalog.i18nc("@label","Please select any upgrades made to this ultimaker original");
+ }
+ CheckBox
+ {
+ text: catalog.i18nc("@label", "Self-built heated bed")
+ checked: manager.hasHeatedBed
+ onClicked: manager.hasHeatedBed ? manager.removeHeatedBed() : manager.addHeatedBed()
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/plugins/UltimakerMachineActions/UpgradeFirmwareMachineAction.py b/plugins/UltimakerMachineActions/UpgradeFirmwareMachineAction.py
index 7d696a871e..53476207fd 100644
--- a/plugins/UltimakerMachineActions/UpgradeFirmwareMachineAction.py
+++ b/plugins/UltimakerMachineActions/UpgradeFirmwareMachineAction.py
@@ -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"
\ No newline at end of file
diff --git a/plugins/UltimakerMachineActions/__init__.py b/plugins/UltimakerMachineActions/__init__.py
index 39734d00e3..fb0b2b1f64 100644
--- a/plugins/UltimakerMachineActions/__init__.py
+++ b/plugins/UltimakerMachineActions/__init__.py
@@ -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()]}
diff --git a/resources/definitions/bq_hephestos_2.def.json b/resources/definitions/bq_hephestos_2.def.json
index e4e58cb6c2..374e41f93c 100644
--- a/resources/definitions/bq_hephestos_2.def.json
+++ b/resources/definitions/bq_hephestos_2.def.json
@@ -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 --\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 },
diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json
index 1a3f8f144a..d187dc66be 100644
--- a/resources/definitions/fdmprinter.def.json
+++ b/resources/definitions/fdmprinter.def.json
@@ -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,8 @@
},
"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,
@@ -69,49 +69,49 @@
},
"material_print_temp_prepend":
{
+ "label": "Wait for material heatup",
"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 +119,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 +150,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 +252,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 +280,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,
@@ -1012,16 +1024,34 @@
"enabled": "retraction_enable",
"settable_per_mesh": true
},
- "retraction_hop": {
- "label": "Z Hop when Retracting",
+ "retraction_hop_enabled": {
+ "label": "Z Hop when Retracted",
"description": "Whenever a retraction is done, the build plate is lowered to create clearance between the nozzle and the print. It prevents the nozzle from hitting the print during travel moves, reducing the chance to knock the print from the build plate.",
- "unit": "mm",
- "type": "float",
- "default_value": 0,
- "minimum_value_warning": "-0.0001",
- "maximum_value_warning": "10",
+ "type": "bool",
+ "default_value": false,
"enabled": "retraction_enable",
- "settable_per_mesh": true
+ "settable_per_mesh": true,
+ "children": {
+ "retraction_hop_only_when_collides": {
+ "label": "Z Hop Only Over Printed Parts",
+ "description": "Only perform a Z Hop when moving over printed parts which cannot be avoided by horizontal motion by Avoid Printed Parts when Traveling.",
+ "type": "bool",
+ "default_value": false,
+ "enabled": "retraction_enable and retraction_hop_enabled and travel_avoid_other_parts",
+ "settable_per_mesh": true
+ },
+ "retraction_hop": {
+ "label": "Z Hop Height",
+ "description": "The height difference when performing a Z Hop.",
+ "unit": "mm",
+ "type": "float",
+ "default_value": 1,
+ "minimum_value_warning": "-0.0001",
+ "maximum_value_warning": "10",
+ "enabled": "retraction_enable and retraction_hop_enabled",
+ "settable_per_mesh": true
+ }
+ }
},
"material_standby_temperature":
{
@@ -1093,16 +1123,12 @@
}
}
},
- "switch_extruder_retraction_hop":
- {
- "label": "Nozzle Switch Z Hop",
- "description": "Whenever the machine switches to another nozzle, the build plate is lowered to create clearance between the nozzle and the print. It prevents the nozzle which has been unused for a while from oozing material on the outside of the print.",
- "type": "float",
- "unit": "mm",
- "default_value": 1,
- "minimum_value_warning": "-0.0001",
- "maximum_value_warning": "10",
- "enabled": "retraction_enable",
+ "retraction_hop_after_extruder_switch": {
+ "label": "Z Hop After Extruder Switch",
+ "description": "After the machine switched from one extruder to the other, the build plate is lowered to create clearance between the nozzle and the print. This prevents the nozzle from leaving oozed material on the outside of a print.",
+ "type": "bool",
+ "default_value": true,
+ "enabled": "retraction_hop_enabled",
"settable_per_mesh": false,
"settable_per_extruder": true
}
@@ -1503,7 +1529,7 @@
"jerk_enabled": {
"label": "Enable Jerk Control",
- "description": "Enables adjusting the jerk of print head when the X or Y axis halts or starts to move. Increasing the jerk can reduce printing time at the cost of print quality.",
+ "description": "Enables adjusting the jerk of print head when the velocity in the X or Y axis changes. Increasing the jerk can reduce printing time at the cost of print quality.",
"type": "bool",
"default_value": false,
"settable_per_mesh": false,
@@ -1511,7 +1537,7 @@
},
"jerk_print": {
"label": "Print Jerk",
- "description": "The maximal allowed jerk of the print head when starting to move or when changing direction.",
+ "description": "The maximum instantaneous velocity change of the print head.",
"unit": "mm/s³",
"type": "float",
"minimum_value": "0.1",
@@ -1523,7 +1549,7 @@
"children": {
"jerk_infill": {
"label": "Infill Jerk",
- "description": "The jerk with which infill is printed.",
+ "description": "The maximum instantaneous velocity change with which infill is printed.",
"unit": "mm/s³",
"type": "float",
"minimum_value": "0.1",
@@ -1536,7 +1562,7 @@
},
"jerk_wall": {
"label": "Wall Jerk",
- "description": "The jerk with which the walls are printed.",
+ "description": "The maximum instantaneous velocity change with which the walls are printed.",
"unit": "mm/s³",
"type": "float",
"minimum_value": "0.1",
@@ -1549,7 +1575,7 @@
"children": {
"jerk_wall_0": {
"label": "Outer Wall Jerk",
- "description": "The jerk with which the outermost walls are printed.",
+ "description": "The maximum instantaneous velocity change with which the outermost walls are printed.",
"unit": "mm/s³",
"type": "float",
"minimum_value": "0.1",
@@ -1562,7 +1588,7 @@
},
"jerk_wall_x": {
"label": "Inner Wall Jerk",
- "description": "The jerk with which all inner walls are printed.",
+ "description": "The maximum instantaneous velocity change with which all inner walls are printed.",
"unit": "mm/s³",
"type": "float",
"minimum_value": "0.1",
@@ -1577,7 +1603,7 @@
},
"jerk_topbottom": {
"label": "Top/Bottom Jerk",
- "description": "The jerk with which top/bottom layers are printed.",
+ "description": "The maximum instantaneous velocity change with which top/bottom layers are printed.",
"unit": "mm/s³",
"type": "float",
"minimum_value": "0.1",
@@ -1590,7 +1616,7 @@
},
"jerk_support": {
"label": "Support Jerk",
- "description": "The jerk with which the support structure is printed.",
+ "description": "The maximum instantaneous velocity change with which the support structure is printed.",
"unit": "mm/s³",
"type": "float",
"minimum_value": "0.1",
@@ -1604,7 +1630,7 @@
"children": {
"jerk_support_infill": {
"label": "Support Infill Jerk",
- "description": "The jerk with which the infill of support is printed.",
+ "description": "The maximum instantaneous velocity change with which the infill of support is printed.",
"unit": "mm/s³",
"type": "float",
"default_value": 20,
@@ -1618,7 +1644,7 @@
},
"jerk_support_roof": {
"label": "Support Roof Jerk",
- "description": "The jerk with which the roofs of support are printed.",
+ "description": "The maximum instantaneous velocity change with which the roofs of support are printed.",
"unit": "mm/s³",
"type": "float",
"default_value": 20,
@@ -1634,7 +1660,7 @@
},
"jerk_prime_tower": {
"label": "Prime Tower Jerk",
- "description": "The jerk with which the prime tower is printed.",
+ "description": "The maximum instantaneous velocity change with which the prime tower is printed.",
"unit": "mm/s³",
"type": "float",
"minimum_value": "0.1",
@@ -1649,7 +1675,7 @@
},
"jerk_travel": {
"label": "Travel Jerk",
- "description": "The jerk with which travel moves are made.",
+ "description": "The maximum instantaneous velocity change with which travel moves are made.",
"unit": "mm/s³",
"type": "float",
"default_value": 30,
@@ -1662,7 +1688,7 @@
},
"jerk_layer_0": {
"label": "Initial Layer Jerk",
- "description": "The print jerk for the initial layer.",
+ "description": "The print maximum instantaneous velocity change for the initial layer.",
"unit": "mm/s³",
"type": "float",
"default_value": 20,
@@ -1675,7 +1701,7 @@
},
"jerk_skirt": {
"label": "Skirt Jerk",
- "description": "The jerk with which the skirt and brim are printed.",
+ "description": "The maximum instantaneous velocity change with which the skirt and brim are printed.",
"unit": "mm/s³",
"type": "float",
"default_value": 20,
@@ -2987,7 +3013,7 @@
{
"label": "Experimental Modes",
"type": "category",
- "icon": "category_blackmagic",
+ "icon": "category_experimental",
"description": "experimental!",
"children":
{
diff --git a/resources/definitions/grr_neo.def.json b/resources/definitions/grr_neo.def.json
index 563bdd1a67..e4b871303b 100644
--- a/resources/definitions/grr_neo.def.json
+++ b/resources/definitions/grr_neo.def.json
@@ -50,10 +50,10 @@
"default_value": "RepRap (Marlin/Sprinter)"
},
"machine_start_gcode": {
- "default_value": "G21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nG1 Z15.0 F9000 ;move the platform down 15mm\nG92 E0 ;zero the extruded length\nG1 F200 E3 ;extrude 3mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F9000\n;Put printing message on LCD screen\nM117 Printing..."
+ "default_value": "G21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nG91 ;relative positioning\nG1 Y-10.0000 F9000 ;compensate firmware head move 1cm right after endstop\nG92 Y0 ;zero Y\nG90 ;absolute positioning\nG1 Z15.0 F9000 ;move the platform down 15mm\nG92 E0 ;zero the extruded length\nG1 F200 E3 ;extrude 3mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F9000\n;Put printing message on LCD screen\nM117 Printing..."
},
"machine_end_gcode": {
"default_value": "M104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning"
}
}
-}
\ No newline at end of file
+}
diff --git a/resources/definitions/mankati_fullscale_xt_plus.def.json b/resources/definitions/mankati_fullscale_xt_plus.def.json
new file mode 100644
index 0000000000..57742696fb
--- /dev/null
+++ b/resources/definitions/mankati_fullscale_xt_plus.def.json
@@ -0,0 +1,66 @@
+{
+ "id": "mankati_fullscale_xt_plus",
+ "version": 2,
+ "name": "Mankati Fullscale XT Plus",
+ "inherits": "fdmprinter",
+ "metadata": {
+ "visible": true,
+ "author": "RBC",
+ "manufacturer": "Mankati",
+ "category": "Other",
+ "file_formats": "text/x-gcode",
+ "platform": "mankati_fullscale_xt_plus_platform.stl"
+ },
+ "overrides": {
+ "machine_width": { "default_value": 260 },
+ "machine_depth": { "default_value": 260 },
+ "machine_height": { "default_value": 300 },
+ "machine_heated_bed": { "default_value": true },
+
+ "machine_nozzle_size": { "default_value": 0.4 },
+ "machine_nozzle_heat_up_speed": { "default_value": 2 },
+ "machine_nozzle_cool_down_speed": { "default_value": 2 },
+ "machine_head_with_fans_polygon": {
+ "default_value": [
+ [ -3, 3 ],
+ [ -3, -3 ],
+ [ 3, -3 ],
+ [ 3, 3 ]
+ ]
+ },
+ "gantry_height": { "default_value": 0 },
+ "machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
+
+ "machine_start_gcode": {
+ "default_value": "M117 Initializing...\nG28 ; home all axes\nG90 ; use absolute coordinates\nG1 F12000 X0 Y0 Z30 ; lift nozzle 30 mm\nT0 ; select extruder 1\nG92 E0 ; reset extruder length\nG1 F100 Z30 E7 ; extrude 7mm while going up\nG92 E0 ; zero the extruder length\nM117 Printing...\n"
+ },
+ "machine_end_gcode": {
+ "default_value": "M104 T0 S0 ; turn off extruder 1 heating\nM140 S0 ; turn off bed heating\nG91 ; relative positioning\nG1 F12000 E-0.5 ; retract 0.5 mm\nG1 F12000 Z30 ; move Z-axes 30 mm down\nG28 X0 Y0 ; home X axis and Y axes\nM84 ; disable motors\nM117 Ready!\n"
+ },
+
+ "layer_height": { "default_value": 0.2 },
+ "wall_thickness": { "default_value": 0.8 },
+ "top_bottom_thickness": { "default_value": 0.3 },
+ "material_print_temperature": { "default_value": 195 },
+ "material_bed_temperature": { "default_value": 60 },
+ "material_diameter": { "default_value": 1.75 },
+ "retraction_enable": { "default_value": true },
+ "retraction_speed": { "default_value": 50 },
+ "retraction_amount": { "default_value": 0.8 },
+ "retraction_hop": { "default_value": 0.075 },
+ "speed_print": { "default_value": 60 },
+ "speed_infill": { "default_value": 100 },
+ "speed_topbottom": { "default_value": 15 },
+ "speed_travel": { "default_value": 150 },
+ "speed_layer_0": {
+ "minimum_value": "0.1",
+ "default": 15.0
+ },
+ "infill_overlap": { "default": 10 },
+ "cool_fan_enabled": { "default": false },
+ "cool_fan_speed": { "default": 0 },
+ "skirt_line_count": { "default": 3 },
+ "skirt_gap": { "default": 4 },
+ "skirt_minimal_length": { "default": 200 }
+ }
+}
diff --git a/resources/definitions/ultimaker_original.def.json b/resources/definitions/ultimaker_original.def.json
index e95431c99e..60eb8dcd00 100644
--- a/resources/definitions/ultimaker_original.def.json
+++ b/resources/definitions/ultimaker_original.def.json
@@ -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": {
diff --git a/resources/meshes/mankati_fullscale_xt_plus_platform.stl b/resources/meshes/mankati_fullscale_xt_plus_platform.stl
new file mode 100644
index 0000000000..049356fd07
Binary files /dev/null and b/resources/meshes/mankati_fullscale_xt_plus_platform.stl differ
diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml
index 5ec6a37ea5..e805991df4 100644
--- a/resources/qml/Cura.qml
+++ b/resources/qml/Cura.qml
@@ -18,6 +18,11 @@ UM.MainWindow
//: Cura application window title
title: catalog.i18nc("@title:window","Cura");
viewportRect: Qt.rect(0, 0, (base.width - sidebar.width) / base.width, 1.0)
+ property bool monitoringPrint: false
+ Component.onCompleted:
+ {
+ Printer.setMinimumWindowSize(UM.Theme.getSize("window_minimum_size"))
+ }
Item
{
@@ -256,17 +261,6 @@ 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
@@ -357,9 +351,57 @@ UM.MainWindow
bottom: parent.bottom;
right: parent.right;
}
-
+ onMonitoringPrintChanged: base.monitoringPrint = monitoringPrint
width: UM.Theme.getSize("sidebar").width;
}
+
+ Rectangle
+ {
+ id: viewportOverlay
+
+ color: UM.Theme.getColor("viewport_overlay")
+ anchors
+ {
+ top: parent.top
+ bottom: parent.bottom
+ left:parent.left
+ right: sidebar.left
+ }
+ visible: opacity > 0
+ opacity: base.monitoringPrint ? 0.75 : 0
+
+ Behavior on opacity { NumberAnimation { duration: 100; } }
+
+ MouseArea {
+ anchors.fill: parent
+ acceptedButtons: Qt.AllButtons
+
+ onWheel: wheel.accepted = true
+ }
+ }
+
+ Image
+ {
+ id: cameraImage
+ width: Math.min(viewportOverlay.width, sourceSize.width)
+ height: sourceSize.height * width / sourceSize.width
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.horizontalCenterOffset: - UM.Theme.getSize("sidebar").width / 2
+ 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;
+ }
+ }
}
}
diff --git a/resources/qml/MonitorButton.qml b/resources/qml/MonitorButton.qml
new file mode 100644
index 0000000000..8a232e62f1
--- /dev/null
+++ b/resources/qml/MonitorButton.qml
@@ -0,0 +1,201 @@
+// 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.1 as UM
+import Cura 1.0 as Cura
+
+Rectangle
+{
+ id: base;
+ UM.I18nCatalog { id: catalog; name:"cura"}
+
+ property bool printerConnected: Cura.MachineManager.printerOutputDevices.length != 0
+ 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")
+ return UM.Theme.getColor("status_busy")
+ else if(Cura.MachineManager.printerOutputDevices[0].jobState == "ready")
+ 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
+ property string statusText:
+ {
+ if(!printerConnected)
+ {
+ return catalog.i18nc("@label:", "Please check your printer connections")
+ } else if(Cura.MachineManager.printerOutputDevices[0].jobState == "printing")
+ {
+ return catalog.i18nc("@label:", "Printing...")
+ } else if(Cura.MachineManager.printerOutputDevices[0].jobState == "paused")
+ {
+ return catalog.i18nc("@label:", "Paused")
+ }
+ else if(Cura.MachineManager.printerOutputDevices[0].jobState == "pre_print")
+ {
+ return catalog.i18nc("@label:", "Preparing...")
+ }
+ else
+ {
+ return " "
+ }
+
+ }
+
+ Label
+ {
+ id: statusLabel
+ width: parent.width - 2 * UM.Theme.getSize("default_margin").width
+ anchors.top: parent.top
+ anchors.left: parent.left
+ anchors.leftMargin: UM.Theme.getSize("default_margin").width
+
+ color: base.statusColor
+ font: UM.Theme.getFont("large")
+ text: statusText;
+ }
+
+ Label
+ {
+ id: percentageLabel
+ anchors.top: parent.top
+ anchors.right: progressBar.right
+
+ color: base.statusColor
+ font: UM.Theme.getFont("large")
+ text: Math.round(progress) + "%";
+ visible: printerConnected
+ }
+
+ Rectangle
+ {
+ id: progressBar
+ width: parent.width - 2 * UM.Theme.getSize("default_margin").width
+ height: UM.Theme.getSize("progressbar").height
+ anchors.top: statusLabel.bottom
+ anchors.topMargin: UM.Theme.getSize("default_margin").height / 4
+ anchors.left: parent.left
+ anchors.leftMargin: UM.Theme.getSize("default_margin").width
+ radius: UM.Theme.getSize("progressbar_radius").width
+ color: UM.Theme.getColor("progressbar_background")
+
+ Rectangle
+ {
+ width: Math.max(parent.width * base.progress / 100)
+ height: parent.height
+ color: base.statusColor
+ radius: UM.Theme.getSize("progressbar_radius").width
+ }
+ }
+
+ Button
+ {
+ id: abortButton
+
+ 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: parent.right
+ anchors.rightMargin: UM.Theme.getSize("default_margin").width
+
+ text: catalog.i18nc("@label:", "Abort Print")
+ onClicked: { Cura.MachineManager.printerOutputDevices[0].setJobState("abort") }
+
+
+ style: ButtonStyle
+ {
+ background: Rectangle
+ {
+ border.width: UM.Theme.getSize("default_lining").width
+ border.color: !control.enabled ? UM.Theme.getColor("action_button_disabled_border") :
+ control.pressed ? UM.Theme.getColor("action_button_active_border") :
+ control.hovered ? UM.Theme.getColor("action_button_hovered_border") : UM.Theme.getColor("action_button_border")
+ color: !control.enabled ? UM.Theme.getColor("action_button_disabled") :
+ control.pressed ? UM.Theme.getColor("action_button_active") :
+ control.hovered ? UM.Theme.getColor("action_button_hovered") : UM.Theme.getColor("action_button")
+ Behavior on color { ColorAnimation { duration: 50; } }
+
+ implicitWidth: actualLabel.contentWidth + (UM.Theme.getSize("default_margin").width * 2)
+
+ Label
+ {
+ id: actualLabel
+ anchors.centerIn: parent
+ color: !control.enabled ? UM.Theme.getColor("action_button_disabled_text") :
+ control.pressed ? UM.Theme.getColor("action_button_active_text") :
+ control.hovered ? UM.Theme.getColor("action_button_hovered_text") : UM.Theme.getColor("action_button_text")
+ font: UM.Theme.getFont("action_button")
+ text: control.text;
+ }
+ }
+ label: Item { }
+ }
+ }
+
+ Button
+ {
+ id: pauseButton
+
+ 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
+ anchors.rightMargin: UM.Theme.getSize("default_margin").width
+
+ text: printerConnected ? Cura.MachineManager.printerOutputDevices[0].jobState == "paused" ? catalog.i18nc("@label:", "Resume") : catalog.i18nc("@label:", "Pause") : ""
+ onClicked: { Cura.MachineManager.printerOutputDevices[0].jobState == "paused" ? Cura.MachineManager.printerOutputDevices[0].setJobState("print") : Cura.MachineManager.printerOutputDevices[0].setJobState("pause") }
+
+ style: ButtonStyle
+ {
+ background: Rectangle
+ {
+ border.width: UM.Theme.getSize("default_lining").width
+ border.color: !control.enabled ? UM.Theme.getColor("action_button_disabled_border") :
+ control.pressed ? UM.Theme.getColor("action_button_active_border") :
+ control.hovered ? UM.Theme.getColor("action_button_hovered_border") : UM.Theme.getColor("action_button_border")
+ color: !control.enabled ? UM.Theme.getColor("action_button_disabled") :
+ control.pressed ? UM.Theme.getColor("action_button_active") :
+ control.hovered ? UM.Theme.getColor("action_button_hovered") : UM.Theme.getColor("action_button")
+ Behavior on color { ColorAnimation { duration: 50; } }
+
+ implicitWidth: actualLabel.contentWidth + (UM.Theme.getSize("default_margin").width * 2)
+
+ Label
+ {
+ id: actualLabel
+ anchors.centerIn: parent
+ color: !control.enabled ? UM.Theme.getColor("action_button_disabled_text") :
+ control.pressed ? UM.Theme.getColor("action_button_active_text") :
+ control.hovered ? UM.Theme.getColor("action_button_hovered_text") : UM.Theme.getColor("action_button_text")
+ font: UM.Theme.getFont("action_button")
+ text: control.text;
+ }
+ }
+ label: Item { }
+ }
+ }
+}
diff --git a/resources/qml/Preferences/MachinesPage.qml b/resources/qml/Preferences/MachinesPage.qml
index 87b4a9e540..14df242f60 100644
--- a/resources/qml/Preferences/MachinesPage.qml
+++ b/resources/qml/Preferences/MachinesPage.qml
@@ -81,7 +81,7 @@ UM.ManagementPage
Repeater
{
id: machineActionRepeater
- model: Cura.MachineActionManager.getSupportedActions(Cura.MachineManager.activeDefinitionId)
+ model: Cura.MachineActionManager.getSupportedActions(Cura.MachineManager.getDefinitionByMachineId(base.currentItem.id))
Button
{
diff --git a/resources/qml/Sidebar.qml b/resources/qml/Sidebar.qml
index 4c8d671a7f..25931932d3 100644
--- a/resources/qml/Sidebar.qml
+++ b/resources/qml/Sidebar.qml
@@ -6,7 +6,7 @@ import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.1
import QtQuick.Layouts 1.1
-import UM 1.1 as UM
+import UM 1.2 as UM
import Cura 1.0 as Cura
Rectangle
@@ -14,6 +14,12 @@ Rectangle
id: base;
property int currentModeIndex;
+ property bool monitoringPrint: false
+ Connections
+ {
+ target: Printer
+ onShowPrintMonitor: base.monitoringPrint = show
+ }
// Is there an output device for this printer?
property bool printerConnected: Cura.MachineManager.printerOutputDevices.length != 0
@@ -21,6 +27,7 @@ Rectangle
color: UM.Theme.getColor("sidebar");
UM.I18nCatalog { id: catalog; name:"cura"}
+
function showTooltip(item, position, text)
{
tooltip.text = text;
@@ -33,6 +40,22 @@ Rectangle
tooltip.hide();
}
+ function strPadLeft(string, pad, length) {
+ return (new Array(length + 1).join(pad) + string).slice(-length);
+ }
+
+ function getPrettyTime(time)
+ {
+ var hours = Math.floor(time / 3600)
+ time -= hours * 3600
+ var minutes = Math.floor(time / 60);
+ time -= minutes * 60
+ var seconds = Math.floor(time);
+
+ var finalTime = strPadLeft(hours, "0", 2) + ':' + strPadLeft(minutes,'0',2)+ ':' + strPadLeft(seconds,'0',2);
+ return finalTime;
+ }
+
MouseArea
{
anchors.fill: parent
@@ -44,12 +67,63 @@ Rectangle
}
}
+ // Mode selection buttons for changing between Setting & Monitor print mode
+ Rectangle
+ {
+ id: sidebarHeaderBar
+ anchors.left: parent.left
+ anchors.right: parent.right
+ height: childrenRect.height
+ color: UM.Theme.getColor("sidebar_header_bar")
+
+ Row
+ {
+ anchors.left: parent.left
+ anchors.leftMargin: UM.Theme.getSize("default_margin").width;
+ anchors.right: parent.right
+ Button
+ {
+ 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: !monitoringPrint
+ exclusiveGroup: sidebarHeaderBarGroup
+
+ style: UM.Theme.styles.sidebar_header_tab
+ }
+ Button
+ {
+ 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 == "printing")
+ return UM.Theme.getIcon("tab_monitor_busy")
+ 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_connected")
+ }
+ checkable: true
+ checked: monitoringPrint
+ exclusiveGroup: sidebarHeaderBarGroup
+
+ style: UM.Theme.styles.sidebar_header_tab
+ }
+ ExclusiveGroup { id: sidebarHeaderBarGroup }
+ }
+ }
+
SidebarHeader {
id: header
width: parent.width
height: totalHeightHeader
- anchors.top: parent.top
+ anchors.top: sidebarHeaderBar.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
onShowTooltip: base.showTooltip(item, location, text)
@@ -85,14 +159,15 @@ Rectangle
Label {
id: settingsModeLabel
- text: catalog.i18nc("@label:listbox","Setup");
+ text: catalog.i18nc("@label:listbox","Print Setup");
anchors.left: parent.left
anchors.leftMargin: UM.Theme.getSize("default_margin").width;
anchors.top: headerSeparator.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
width: parent.width/100*45
- font: UM.Theme.getFont("large");
+ font: UM.Theme.getFont("large")
color: UM.Theme.getColor("text")
+ visible: !monitoringPrint
}
Rectangle {
@@ -103,6 +178,7 @@ Rectangle
anchors.rightMargin: UM.Theme.getSize("default_margin").width
anchors.top: headerSeparator.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
+ visible: !monitoringPrint
Component{
id: wizardDelegate
Button {
@@ -152,6 +228,19 @@ Rectangle
}
}
+ Label {
+ id: monitorLabel
+ text: catalog.i18nc("@label","Printer Monitor");
+ anchors.left: parent.left
+ anchors.leftMargin: UM.Theme.getSize("default_margin").width;
+ anchors.top: headerSeparator.bottom
+ anchors.topMargin: UM.Theme.getSize("default_margin").height
+ width: parent.width/100*45
+ font: UM.Theme.getFont("large")
+ color: UM.Theme.getColor("text")
+ visible: monitoringPrint
+ }
+
StackView
{
id: sidebarContents
@@ -161,6 +250,7 @@ Rectangle
anchors.topMargin: UM.Theme.getSize("default_margin").height
anchors.left: base.left
anchors.right: base.right
+ visible: !monitoringPrint
delegate: StackViewDelegate
{
@@ -191,23 +281,152 @@ Rectangle
}
}
- Rectangle {
+ // Item that shows the print monitor properties
+ Column
+ {
+ 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")
+ }
+ }
+ }
+ }
+
+ Rectangle
+ {
id: footerSeparator
width: parent.width
height: UM.Theme.getSize("sidebar_lining").height
color: UM.Theme.getColor("sidebar_lining")
anchors.bottom: saveButton.top
- anchors.bottomMargin: UM.Theme.getSize("default_margin").height
+ anchors.bottomMargin: UM.Theme.getSize("default_margin").height
}
SaveButton
{
- id: saveButton;
+ id: saveButton
implicitWidth: base.width
implicitHeight: totalHeight
anchors.bottom: parent.bottom
+ visible: !monitoringPrint
}
+ MonitorButton
+ {
+ id: monitorButton
+ implicitWidth: base.width
+ implicitHeight: totalHeight
+ anchors.bottom: parent.bottom
+ visible: monitoringPrint
+ }
+
+
SidebarTooltip
{
id: tooltip;
@@ -242,4 +461,24 @@ Rectangle
modesListModel.append({ text: catalog.i18nc("@title:tab", "Advanced"), item: sidebarAdvanced })
sidebarContents.push({ "item": modesListModel.get(base.currentModeIndex).item, "immediate": true });
}
-}
+
+ UM.SettingPropertyProvider
+ {
+ id: machineExtruderCount
+
+ containerStackId: Cura.MachineManager.activeMachineId
+ key: "machine_extruder_count"
+ watchedProperties: [ "value" ]
+ storeIndex: 0
+ }
+
+ UM.SettingPropertyProvider
+ {
+ id: machineHeatedBed
+
+ containerStackId: Cura.MachineManager.activeMachineId
+ key: "machine_heated_bed"
+ watchedProperties: [ "value" ]
+ storeIndex: 0
+ }
+}
\ No newline at end of file
diff --git a/resources/qml/SidebarHeader.qml b/resources/qml/SidebarHeader.qml
index 4aad341ee0..607f0dedfe 100644
--- a/resources/qml/SidebarHeader.qml
+++ b/resources/qml/SidebarHeader.qml
@@ -92,7 +92,8 @@ Column
onGlobalContainerChanged:
{
base.currentExtruderIndex = -1;
- ExtruderManager.setActiveExtruderIndex(index);
+ forceActiveFocus()
+ ExtruderManager.setActiveExtruderIndex(0);
}
}
@@ -109,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);
}
diff --git a/resources/themes/cura/icons/category_experimental.svg b/resources/themes/cura/icons/category_experimental.svg
new file mode 100644
index 0000000000..4890353b01
--- /dev/null
+++ b/resources/themes/cura/icons/category_experimental.svg
@@ -0,0 +1,81 @@
+
+
+
+
\ No newline at end of file
diff --git a/resources/themes/cura/icons/category_shell.svg b/resources/themes/cura/icons/category_shell.svg
index 7ddf7f5bde..6f71c80e63 100644
--- a/resources/themes/cura/icons/category_shell.svg
+++ b/resources/themes/cura/icons/category_shell.svg
@@ -1,5345 +1,88 @@
-
-
-
-
-
-
-
-
-
-
-]>
-
+
+
+
+
\ No newline at end of file
diff --git a/resources/themes/cura/icons/tab_monitor.svg b/resources/themes/cura/icons/tab_monitor.svg
new file mode 100644
index 0000000000..516191b988
--- /dev/null
+++ b/resources/themes/cura/icons/tab_monitor.svg
@@ -0,0 +1,75 @@
+
+
diff --git a/resources/themes/cura/icons/tab_monitor_busy.svg b/resources/themes/cura/icons/tab_monitor_busy.svg
new file mode 100644
index 0000000000..f0e47b91d4
--- /dev/null
+++ b/resources/themes/cura/icons/tab_monitor_busy.svg
@@ -0,0 +1,107 @@
+
+
diff --git a/resources/themes/cura/icons/tab_monitor_connected.svg b/resources/themes/cura/icons/tab_monitor_connected.svg
new file mode 100644
index 0000000000..8f6f83fe91
--- /dev/null
+++ b/resources/themes/cura/icons/tab_monitor_connected.svg
@@ -0,0 +1,95 @@
+
+
diff --git a/resources/themes/cura/icons/tab_monitor_offline.svg b/resources/themes/cura/icons/tab_monitor_offline.svg
new file mode 100644
index 0000000000..a13a7b586e
--- /dev/null
+++ b/resources/themes/cura/icons/tab_monitor_offline.svg
@@ -0,0 +1,94 @@
+
+
diff --git a/resources/themes/cura/icons/tab_monitor_paused.svg b/resources/themes/cura/icons/tab_monitor_paused.svg
new file mode 100644
index 0000000000..49c8babebd
--- /dev/null
+++ b/resources/themes/cura/icons/tab_monitor_paused.svg
@@ -0,0 +1,103 @@
+
+
diff --git a/resources/themes/cura/icons/tab_monitor_stopped.svg b/resources/themes/cura/icons/tab_monitor_stopped.svg
new file mode 100644
index 0000000000..7f861067dd
--- /dev/null
+++ b/resources/themes/cura/icons/tab_monitor_stopped.svg
@@ -0,0 +1,94 @@
+
+
diff --git a/resources/themes/cura/icons/tab_settings.svg b/resources/themes/cura/icons/tab_settings.svg
new file mode 100644
index 0000000000..f181147d98
--- /dev/null
+++ b/resources/themes/cura/icons/tab_settings.svg
@@ -0,0 +1,75 @@
+
+
diff --git a/resources/themes/cura/styles.qml b/resources/themes/cura/styles.qml
index 3fdc7c896e..8d813bc2b5 100644
--- a/resources/themes/cura/styles.qml
+++ b/resources/themes/cura/styles.qml
@@ -44,6 +44,56 @@ QtObject {
}
}
+ property Component sidebar_header_tab: Component {
+ ButtonStyle {
+ background: Item {
+ implicitWidth: Theme.getSize("button").width;
+ implicitHeight: Theme.getSize("button").height;
+
+ Rectangle {
+ id: buttonFace;
+
+ anchors.fill: parent;
+ property bool down: control.pressed || (control.checkable && control.checked);
+
+ color: {
+ if(control.pressed || (control.checkable && control.checked)) {
+ return Theme.getColor("sidebar_header_active");
+ } else if(control.hovered) {
+ return Theme.getColor("sidebar_header_hover");
+ } else {
+ return Theme.getColor("sidebar_header_bar");
+ }
+ }
+ Behavior on color { ColorAnimation { duration: 50; } }
+
+ Rectangle {
+ id: underline;
+
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ height: UM.Theme.getSize("sidebar_header_highlight").height
+ color: control.checked ? UM.Theme.getColor("sidebar_header_highlight") : UM.Theme.getColor("sidebar_header_highlight_hover")
+ visible: control.hovered || control.checked
+ }
+ }
+ }
+
+ label: Item {
+ Image {
+ anchors.centerIn: parent;
+ opacity: !control.enabled ? 0.2 : 1.0
+ source: control.iconSource;
+ width: Theme.getSize("button_icon").width;
+ height: Theme.getSize("button_icon").height;
+
+ sourceSize: Theme.getSize("button_icon")
+ }
+ }
+ }
+ }
+
property Component tool_button: Component {
ButtonStyle {
background: Item {
diff --git a/resources/themes/cura/theme.json b/resources/themes/cura/theme.json
index acd60e2646..5bc5e99765 100644
--- a/resources/themes/cura/theme.json
+++ b/resources/themes/cura/theme.json
@@ -38,6 +38,7 @@
"colors": {
"sidebar": [255, 255, 255, 255],
"lining": [127, 127, 127, 255],
+ "viewport_overlay": [24, 41, 77, 255],
"primary": [12, 169, 227, 255],
"primary_hover": [48, 182, 231, 255],
@@ -55,6 +56,10 @@
"error": [255, 140, 0, 255],
"sidebar_header_bar": [24, 41, 77, 255],
+ "sidebar_header_active": [70, 84, 113, 255],
+ "sidebar_header_hover": [24, 41, 77, 255],
+ "sidebar_header_highlight": [12, 169, 227, 255],
+ "sidebar_header_highlight_hover": [255, 255, 255, 255],
"sidebar_lining": [245, 245, 245, 255],
"button": [24, 41, 77, 255],
@@ -153,10 +158,17 @@
"message_text": [32, 166, 219, 255],
"message_dismiss": [127, 127, 127, 255],
- "tool_panel_background": [255, 255, 255, 255]
+ "tool_panel_background": [255, 255, 255, 255],
+
+ "status_offline": [0, 0, 0, 255],
+ "status_ready": [0, 205, 0, 255],
+ "status_busy": [12, 169, 227, 255],
+ "status_paused": [255, 140, 0, 255],
+ "status_stopped": [236, 82, 80, 255]
},
"sizes": {
+ "window_minimum_size": [70, 54],
"window_margin": [1.0, 1.0],
"default_margin": [1.0, 1.0],
"default_lining": [0.08, 0.08],
@@ -165,6 +177,7 @@
"sidebar": [35.0, 10.0],
"sidebar_header": [0.0, 4.0],
+ "sidebar_header_highlight": [0.5, 0.5],
"sidebar_header_mode_toggle": [0.0, 2.0],
"sidebar_lining": [0.5, 0.5],
"sidebar_setup": [0.0, 2.0],