Browse Source

Merge pull request #822 from Ultimaker/feature_print_monitoring

Print monitor
Aldo Hoeben 8 years ago
parent
commit
d1b1741d67

+ 18 - 0
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)

+ 5 - 1
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 PyQt5.QtCore import pyqtSlot, QUrl, pyqtSignal, pyqtProperty, QEvent, Q_ENUMS
@@ -229,7 +230,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 +239,9 @@ class CuraApplication(QtApplication):
 
             self._recent_files.append(QUrl.fromLocalFile(f))
 
+    def _onEngineCreated(self):
+        self._engine.addImageProvider("camera", CameraImageProvider.CameraImageProvider())
+
     ##  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.

+ 61 - 0
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

+ 39 - 2
resources/qml/Cura.qml

@@ -16,7 +16,7 @@ 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"))
@@ -536,9 +536,46 @@ 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 : ""
+            }
         }
     }
 

+ 183 - 0
resources/qml/MonitorButton.qml

@@ -0,0 +1,183 @@
+// 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 bool activity: Printer.getPlatformActivity;
+    property int totalHeight: childrenRect.height + UM.Theme.getSize("default_margin").height
+    property string fileBaseName
+    property string statusText:
+    {
+        if(!printerConnected)
+        {
+            return "Please check your printer connections"
+        } else if(Cura.MachineManager.printerOutputDevices[0].jobState == "printing")
+        {
+            return "Printing..."
+        } else if(Cura.MachineManager.printerOutputDevices[0].jobState == "paused")
+        {
+            return "Paused"
+        }
+        else if(Cura.MachineManager.printerOutputDevices[0].jobState == "pre_print")
+        {
+            return "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: printerConnected ? Cura.MachineManager.printerOutputDevices[0].jobState == "paused" ? UM.Theme.getColor("status_paused") : UM.Theme.getColor("status_ready") : UM.Theme.getColor("status_offline")
+        font: UM.Theme.getFont("large")
+        text: statusText;
+    }
+
+    Label
+    {
+        id: percentageLabel
+        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")
+        font: UM.Theme.getFont("large")
+        text: Math.round(progress * 100) + "%";
+        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)
+            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")
+            radius: UM.Theme.getSize("progressbar_radius").width
+        }
+    }
+
+    Button
+    {
+        id: abortButton
+
+        visible: printerConnected
+        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
+
+        height: UM.Theme.getSize("save_button_save_to_button").height
+        visible: printerConnected
+
+        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 { }
+        }
+    }
+}

+ 243 - 8
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,7 @@ Rectangle
     id: base;
 
     property int currentModeIndex;
+    property bool monitoringPrint: false
 
     // Is there an output device for this printer?
     property bool printerConnected: Cura.MachineManager.printerOutputDevices.length != 0
@@ -21,6 +22,7 @@ Rectangle
     color: UM.Theme.getColor("sidebar");
     UM.I18nCatalog { id: catalog; name:"cura"}
 
+
     function showTooltip(item, position, text)
     {
         tooltip.text = text;
@@ -33,6 +35,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 +62,64 @@ 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: true
+                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 == "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
+                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 +155,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 +174,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 +224,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 +246,7 @@ Rectangle
         anchors.topMargin: UM.Theme.getSize("default_margin").height
         anchors.left: base.left
         anchors.right: base.right
+        visible: !monitoringPrint
 
         delegate: StackViewDelegate
         {
@@ -191,23 +277,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 +457,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
+    }
+}

+ 75 - 0
resources/themes/cura/icons/tab_monitor.svg

@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="30"
+   height="30"
+   viewBox="0 0 30 30"
+   version="1.1"
+   id="svg2"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="tab_monitor.svg">
+  <metadata
+     id="metadata15">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title>Fill 1 Copy 3</dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <sodipodi:namedview
+     pagecolor="#000000"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="769"
+     inkscape:window-height="716"
+     id="namedview13"
+     showgrid="false"
+     showborder="true"
+     inkscape:zoom="12.421053"
+     inkscape:cx="19.963695"
+     inkscape:cy="9.5"
+     inkscape:window-x="549"
+     inkscape:window-y="180"
+     inkscape:window-maximized="0"
+     inkscape:current-layer="svg2" />
+  <!-- Generator: Sketch 3.4.4 (17249) - http://www.bohemiancoding.com/sketch -->
+  <title
+     id="title4">Fill 1 Copy 3</title>
+  <desc
+     id="desc6">Created with Sketch.</desc>
+  <defs
+     id="defs8" />
+  <g
+     id="Page-1"
+     sketch:type="MSPage"
+     style="fill:none;fill-rule:evenodd;stroke:none;stroke-width:1"
+     transform="matrix(1.3157895,0,0,1.3157895,2.5,2.4999995)">
+    <g
+       id="HIG"
+       sketch:type="MSArtboardGroup"
+       transform="translate(-718,-2432)"
+       style="fill:#ffffff">
+      <path
+         d="m 718,2432 19,0 0,0.9048 -19,0 0,-0.9048 0,0 z m 0,18.0952 1.73776,0 1.7267,-0.9047 12.13477,0 1.69775,0.9047 1.70302,0 0,0.9048 -1.70166,0 -1.69911,-0.9048 -12.13593,0 -1.72554,0.8949 L 718,2451 l 0,-0.9048 0,0 z m 18.13636,-17.1904 0.86364,0 0,17.1904 -0.86364,0 0,-17.1904 0,0 z m -18.13636,0 0.86364,0 0,17.1904 -0.86364,0 0,-17.1904 0,0 z m 2.59091,1.8095 13.81818,0 0,12.6667 -13.81818,0 0,-12.6667 0,0 z m 0.86364,0.9047 12.0909,0 0,10.8572 -12.0909,0 0,-10.8572 0,0 z m 4.31818,0 3.45454,0 0,2.7143 -3.45454,0 0,-2.7143 0,0 z m -2.59091,9.9524 8.63636,0 0,0.9048 -8.63636,0 0,-0.9048 0,0 z m 3.45454,-7.2381 1.72728,0 0,0.9048 -1.72728,0 0,-0.9048 0,0 z"
+         id="Fill-1-Copy-3"
+         sketch:type="MSShapeGroup"
+         inkscape:connector-curvature="0" />
+    </g>
+  </g>
+</svg>

+ 107 - 0
resources/themes/cura/icons/tab_monitor_busy.svg

@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="30"
+   height="30"
+   viewBox="0 0 30 30"
+   version="1.1"
+   id="svg2"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="tab_monitor_busy.svg">
+  <metadata
+     id="metadata15">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title>Fill 1 Copy 3</dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <sodipodi:namedview
+     pagecolor="#000000"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="1280"
+     inkscape:window-height="748"
+     id="namedview13"
+     showgrid="false"
+     showborder="true"
+     inkscape:zoom="18.366667"
+     inkscape:cx="15.462795"
+     inkscape:cy="15.381125"
+     inkscape:window-x="-8"
+     inkscape:window-y="-8"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg2"
+     inkscape:snap-global="true"
+     inkscape:object-nodes="false"
+     inkscape:snap-smooth-nodes="false"
+     inkscape:snap-midpoints="false"
+     inkscape:snap-intersection-paths="false"
+     inkscape:snap-bbox="true"
+     inkscape:snap-others="false"
+     inkscape:snap-nodes="false" />
+  <!-- Generator: Sketch 3.4.4 (17249) - http://www.bohemiancoding.com/sketch -->
+  <title
+     id="title4">Fill 1 Copy 3</title>
+  <desc
+     id="desc6">Created with Sketch.</desc>
+  <defs
+     id="defs8" />
+  <g
+     id="Page-1"
+     sketch:type="MSPage"
+     style="fill:none;fill-rule:evenodd;stroke:none;stroke-width:1"
+     transform="matrix(1.3157895,0,0,1.3157895,2.5,2.4999995)">
+    <g
+       id="HIG"
+       sketch:type="MSArtboardGroup"
+       transform="translate(-718,-2432)"
+       style="fill:#ffffff">
+      <path
+         d="m 718,2432 19,0 0,0.9048 -19,0 0,-0.9048 0,0 z m 0,18.0952 1.73776,0 1.7267,-0.9047 12.13477,0 1.69775,0.9047 1.70302,0 0,0.9048 -1.70166,0 -1.69911,-0.9048 -12.13593,0 -1.72554,0.8949 L 718,2451 l 0,-0.9048 0,0 z m 18.13636,-17.1904 0.86364,0 0,17.1904 -0.86364,0 0,-17.1904 0,0 z m -18.13636,0 0.86364,0 0,17.1904 -0.86364,0 0,-17.1904 0,0 z m 2.59091,1.8095 13.81818,0 0,12.6667 -13.81818,0 0,-12.6667 0,0 z m 0.86364,0.9047 12.0909,0 0,10.8572 -12.0909,0 0,-10.8572 0,0 z m 4.31818,0 3.45454,0 0,2.7143 -3.45454,0 0,-2.7143 0,0 z m -2.59091,9.9524 8.63636,0 0,0.9048 -8.63636,0 0,-0.9048 0,0 z m 3.45454,-7.2381 1.72728,0 0,0.9048 -1.72728,0 0,-0.9048 0,0 z"
+         id="Fill-1-Copy-3"
+         sketch:type="MSShapeGroup"
+         inkscape:connector-curvature="0" />
+    </g>
+  </g>
+  <circle
+     style="fill:#0ca9e3;fill-opacity:1"
+     id="path3337"
+     cx="22.5"
+     cy="7.5"
+     r="7.5" />
+  <circle
+     style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-opacity:1"
+     id="path4759"
+     cx="22.5"
+     cy="7.5"
+     r="1.5" />
+  <circle
+     r="1.5"
+     cy="7.5"
+     cx="18.5"
+     id="circle4761"
+     style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-opacity:1" />
+  <circle
+     style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-opacity:1"
+     id="circle4763"
+     cx="26.5"
+     cy="7.5"
+     r="1.5" />
+</svg>

+ 95 - 0
resources/themes/cura/icons/tab_monitor_connected.svg

@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="30"
+   height="30"
+   viewBox="0 0 30 30"
+   version="1.1"
+   id="svg2"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="tab_monitor_connected.svg">
+  <metadata
+     id="metadata15">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title>Fill 1 Copy 3</dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <sodipodi:namedview
+     pagecolor="#000000"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="1280"
+     inkscape:window-height="748"
+     id="namedview13"
+     showgrid="false"
+     showborder="true"
+     inkscape:zoom="18.366667"
+     inkscape:cx="15"
+     inkscape:cy="15"
+     inkscape:window-x="-8"
+     inkscape:window-y="-8"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg2"
+     inkscape:snap-global="true"
+     inkscape:object-nodes="false"
+     inkscape:snap-smooth-nodes="false"
+     inkscape:snap-midpoints="false"
+     inkscape:snap-intersection-paths="false"
+     inkscape:snap-bbox="true"
+     inkscape:snap-others="false"
+     inkscape:snap-nodes="false" />
+  <!-- Generator: Sketch 3.4.4 (17249) - http://www.bohemiancoding.com/sketch -->
+  <title
+     id="title4">Fill 1 Copy 3</title>
+  <desc
+     id="desc6">Created with Sketch.</desc>
+  <defs
+     id="defs8" />
+  <g
+     id="Page-1"
+     sketch:type="MSPage"
+     style="fill:none;fill-rule:evenodd;stroke:none;stroke-width:1"
+     transform="matrix(1.3157895,0,0,1.3157895,2.5,2.4999995)">
+    <g
+       id="HIG"
+       sketch:type="MSArtboardGroup"
+       transform="translate(-718,-2432)"
+       style="fill:#ffffff">
+      <path
+         d="m 718,2432 19,0 0,0.9048 -19,0 0,-0.9048 0,0 z m 0,18.0952 1.73776,0 1.7267,-0.9047 12.13477,0 1.69775,0.9047 1.70302,0 0,0.9048 -1.70166,0 -1.69911,-0.9048 -12.13593,0 -1.72554,0.8949 L 718,2451 l 0,-0.9048 0,0 z m 18.13636,-17.1904 0.86364,0 0,17.1904 -0.86364,0 0,-17.1904 0,0 z m -18.13636,0 0.86364,0 0,17.1904 -0.86364,0 0,-17.1904 0,0 z m 2.59091,1.8095 13.81818,0 0,12.6667 -13.81818,0 0,-12.6667 0,0 z m 0.86364,0.9047 12.0909,0 0,10.8572 -12.0909,0 0,-10.8572 0,0 z m 4.31818,0 3.45454,0 0,2.7143 -3.45454,0 0,-2.7143 0,0 z m -2.59091,9.9524 8.63636,0 0,0.9048 -8.63636,0 0,-0.9048 0,0 z m 3.45454,-7.2381 1.72728,0 0,0.9048 -1.72728,0 0,-0.9048 0,0 z"
+         id="Fill-1-Copy-3"
+         sketch:type="MSShapeGroup"
+         inkscape:connector-curvature="0" />
+    </g>
+  </g>
+  <circle
+     style="fill:#00cd00;fill-opacity:1"
+     id="path3337"
+     cx="22.5"
+     cy="7.5"
+     r="7.5" />
+  <path
+     style="fill:#ffffff;fill-opacity:1"
+     d="M 25.546876,4.3017582 21.994141,7.9208992 19.859375,5.8408211 18.5,7.2177742 22.072266,10.698242 C 23.732574,9.0171252 25.519159,7.1853281 26.97461,5.7021492 Z"
+     id="rect4171"
+     inkscape:connector-curvature="0"
+     sodipodi:nodetypes="ccccccc" />
+</svg>

+ 94 - 0
resources/themes/cura/icons/tab_monitor_offline.svg

@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="30"
+   height="30"
+   viewBox="0 0 30 30"
+   version="1.1"
+   id="svg2"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="tab_monitor_offline.svg">
+  <metadata
+     id="metadata15">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title>Fill 1 Copy 3</dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <sodipodi:namedview
+     pagecolor="#000000"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="1280"
+     inkscape:window-height="748"
+     id="namedview13"
+     showgrid="false"
+     showborder="true"
+     inkscape:zoom="18.366667"
+     inkscape:cx="15.462795"
+     inkscape:cy="15.381125"
+     inkscape:window-x="-8"
+     inkscape:window-y="-8"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg2"
+     inkscape:snap-global="true"
+     inkscape:object-nodes="false"
+     inkscape:snap-smooth-nodes="false"
+     inkscape:snap-midpoints="false"
+     inkscape:snap-intersection-paths="false"
+     inkscape:snap-bbox="true"
+     inkscape:snap-others="false"
+     inkscape:snap-nodes="false" />
+  <!-- Generator: Sketch 3.4.4 (17249) - http://www.bohemiancoding.com/sketch -->
+  <title
+     id="title4">Fill 1 Copy 3</title>
+  <desc
+     id="desc6">Created with Sketch.</desc>
+  <defs
+     id="defs8" />
+  <g
+     id="Page-1"
+     sketch:type="MSPage"
+     style="fill:none;fill-rule:evenodd;stroke:none;stroke-width:1"
+     transform="matrix(1.3157895,0,0,1.3157895,2.5,2.4999995)">
+    <g
+       id="HIG"
+       sketch:type="MSArtboardGroup"
+       transform="translate(-718,-2432)"
+       style="fill:#ffffff">
+      <path
+         d="m 718,2432 19,0 0,0.9048 -19,0 0,-0.9048 0,0 z m 0,18.0952 1.73776,0 1.7267,-0.9047 12.13477,0 1.69775,0.9047 1.70302,0 0,0.9048 -1.70166,0 -1.69911,-0.9048 -12.13593,0 -1.72554,0.8949 L 718,2451 l 0,-0.9048 0,0 z m 18.13636,-17.1904 0.86364,0 0,17.1904 -0.86364,0 0,-17.1904 0,0 z m -18.13636,0 0.86364,0 0,17.1904 -0.86364,0 0,-17.1904 0,0 z m 2.59091,1.8095 13.81818,0 0,12.6667 -13.81818,0 0,-12.6667 0,0 z m 0.86364,0.9047 12.0909,0 0,10.8572 -12.0909,0 0,-10.8572 0,0 z m 4.31818,0 3.45454,0 0,2.7143 -3.45454,0 0,-2.7143 0,0 z m -2.59091,9.9524 8.63636,0 0,0.9048 -8.63636,0 0,-0.9048 0,0 z m 3.45454,-7.2381 1.72728,0 0,0.9048 -1.72728,0 0,-0.9048 0,0 z"
+         id="Fill-1-Copy-3"
+         sketch:type="MSShapeGroup"
+         inkscape:connector-curvature="0" />
+    </g>
+  </g>
+  <circle
+     style="fill:#000000;fill-opacity:1"
+     id="path3337"
+     cx="22.5"
+     cy="7.5"
+     r="7.5" />
+  <path
+     style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-opacity:1"
+     d="M 20.02539,3.6113281 18.611328,5.0253907 21.085937,7.5 18.611328,9.9746094 20.02539,11.388672 22.5,8.9140625 24.974609,11.388672 26.388672,9.9746094 23.914062,7.5 26.388672,5.0253907 24.974609,3.6113281 22.5,6.0859375 20.02539,3.6113281 Z"
+     id="rect4713-3"
+     inkscape:connector-curvature="0" />
+</svg>

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