Merge pull request #4967 from Ultimaker/ui_rework_4_0

Merge the entire UI rework planned for the new redesign of Cura 4.0
This commit is contained in:
Diego Prado Gesto 2018-12-11 12:17:55 +01:00 committed by GitHub
commit 4c82f8b74f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
204 changed files with 11335 additions and 8853 deletions

View file

@ -38,12 +38,13 @@ class Account(QObject):
self._callback_port = 32118 self._callback_port = 32118
self._oauth_root = "https://account.ultimaker.com" self._oauth_root = "https://account.ultimaker.com"
self._cloud_api_root = "https://api.ultimaker.com"
self._oauth_settings = OAuth2Settings( self._oauth_settings = OAuth2Settings(
OAUTH_SERVER_URL= self._oauth_root, OAUTH_SERVER_URL= self._oauth_root,
CALLBACK_PORT=self._callback_port, CALLBACK_PORT=self._callback_port,
CALLBACK_URL="http://localhost:{}/callback".format(self._callback_port), CALLBACK_URL="http://localhost:{}/callback".format(self._callback_port),
CLIENT_ID="um----------------------------ultimaker_cura", CLIENT_ID="um---------------ultimaker_cura_drive_plugin",
CLIENT_SCOPES="account.user.read drive.backup.read drive.backup.write packages.download packages.rating.read packages.rating.write", CLIENT_SCOPES="account.user.read drive.backup.read drive.backup.write packages.download packages.rating.read packages.rating.write",
AUTH_DATA_PREFERENCE_KEY="general/ultimaker_auth_data", AUTH_DATA_PREFERENCE_KEY="general/ultimaker_auth_data",
AUTH_SUCCESS_REDIRECT="{}/app/auth-success".format(self._oauth_root), AUTH_SUCCESS_REDIRECT="{}/app/auth-success".format(self._oauth_root),

View file

@ -441,6 +441,7 @@ class CuraApplication(QtApplication):
"XmlMaterialProfile", #Cura crashes without this one. "XmlMaterialProfile", #Cura crashes without this one.
"Toolbox", #This contains the interface to enable/disable plug-ins, so if you disable it you can't enable it back. "Toolbox", #This contains the interface to enable/disable plug-ins, so if you disable it you can't enable it back.
"PrepareStage", #Cura is useless without this one since you can't load models. "PrepareStage", #Cura is useless without this one since you can't load models.
"PreviewStage", #This shows the list of the plugin views that are installed in Cura.
"MonitorStage", #Major part of Cura's functionality. "MonitorStage", #Major part of Cura's functionality.
"LocalFileOutputDevice", #Major part of Cura's functionality. "LocalFileOutputDevice", #Major part of Cura's functionality.
"LocalContainerProvider", #Cura is useless without any profiles or setting definitions. "LocalContainerProvider", #Cura is useless without any profiles or setting definitions.
@ -633,9 +634,7 @@ class CuraApplication(QtApplication):
self._message_box_callback(button, *self._message_box_callback_arguments) self._message_box_callback(button, *self._message_box_callback_arguments)
self._message_box_callback = None self._message_box_callback = None
self._message_box_callback_arguments = [] self._message_box_callback_arguments = []
showPrintMonitor = pyqtSignal(bool, arguments = ["show"])
def setSaveDataEnabled(self, enabled: bool) -> None: def setSaveDataEnabled(self, enabled: bool) -> None:
self._save_data_enabled = enabled self._save_data_enabled = enabled
@ -1666,7 +1665,9 @@ class CuraApplication(QtApplication):
is_non_sliceable = "." + file_extension in self._non_sliceable_extensions is_non_sliceable = "." + file_extension in self._non_sliceable_extensions
if is_non_sliceable: if is_non_sliceable:
self.callLater(lambda: self.getController().setActiveView("SimulationView")) # Need to switch first to the preview stage and then to layer view
self.callLater(lambda: (self.getController().setActiveStage("PreviewStage"),
self.getController().setActiveView("SimulationView")))
block_slicing_decorator = BlockSlicingDecorator() block_slicing_decorator = BlockSlicingDecorator()
node.addDecorator(block_slicing_decorator) node.addDecorator(block_slicing_decorator)

24
cura/CuraView.py Normal file
View file

@ -0,0 +1,24 @@
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from PyQt5.QtCore import pyqtProperty, QUrl
from UM.View.View import View
# Since Cura has a few pre-defined "space claims" for the locations of certain components, we've provided some structure
# to indicate this.
# MainComponent works in the same way the MainComponent of a stage.
# the stageMenuComponent returns an item that should be used somehwere in the stage menu. It's up to the active stage
# to actually do something with this.
class CuraView(View):
def __init__(self, parent = None) -> None:
super().__init__(parent)
@pyqtProperty(QUrl, constant = True)
def mainComponent(self) -> QUrl:
return self.getDisplayComponent("main")
@pyqtProperty(QUrl, constant = True)
def stageMenuComponent(self) -> QUrl:
return self.getDisplayComponent("menu")

View file

@ -1,4 +1,6 @@
# Copyright (c) 2018 Ultimaker B.V. # Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from typing import Optional from typing import Optional

View file

@ -394,21 +394,7 @@ class PrintInformation(QObject):
return return
active_machine_type_name = global_container_stack.definition.getName() active_machine_type_name = global_container_stack.definition.getName()
abbr_machine = "" self._abbr_machine = self._application.getMachineManager().getAbbreviatedMachineName(active_machine_type_name)
for word in re.findall(r"[\w']+", active_machine_type_name):
if word.lower() == "ultimaker":
abbr_machine += "UM"
elif word.isdigit():
abbr_machine += word
else:
stripped_word = self._stripAccents(word.upper())
# - use only the first character if the word is too long (> 3 characters)
# - use the whole word if it's not too long (<= 3 characters)
if len(stripped_word) > 3:
stripped_word = stripped_word[0]
abbr_machine += stripped_word
self._abbr_machine = abbr_machine
## Utility method that strips accents from characters (eg: â -> a) ## Utility method that strips accents from characters (eg: â -> a)
def _stripAccents(self, to_strip: str) -> str: def _stripAccents(self, to_strip: str) -> str:

View file

@ -211,6 +211,11 @@ class PrinterOutputDevice(QObject, OutputDevice):
self._unique_configurations.sort(key = lambda k: k.printerType) self._unique_configurations.sort(key = lambda k: k.printerType)
self.uniqueConfigurationsChanged.emit() self.uniqueConfigurationsChanged.emit()
# Returns the unique configurations of the printers within this output device
@pyqtProperty("QStringList", notify = uniqueConfigurationsChanged)
def uniquePrinterTypes(self) -> List[str]:
return list(set([configuration.printerType for configuration in self._unique_configurations]))
def _onPrintersChanged(self) -> None: def _onPrintersChanged(self) -> None:
for printer in self._printers: for printer in self._printers:
printer.configurationChanged.connect(self._updateUniqueConfigurations) printer.configurationChanged.connect(self._updateUniqueConfigurations)
@ -238,4 +243,4 @@ class PrinterOutputDevice(QObject, OutputDevice):
if not self._firmware_updater: if not self._firmware_updater:
return return
self._firmware_updater.updateFirmware(firmware_file) self._firmware_updater.updateFirmware(firmware_file)

View file

@ -63,7 +63,7 @@ class ExtruderManager(QObject):
if not self._application.getGlobalContainerStack(): if not self._application.getGlobalContainerStack():
return None # No active machine, so no active extruder. return None # No active machine, so no active extruder.
try: try:
return self._extruder_trains[self._application.getGlobalContainerStack().getId()][str(self._active_extruder_index)].getId() return self._extruder_trains[self._application.getGlobalContainerStack().getId()][str(self.activeExtruderIndex)].getId()
except KeyError: # Extruder index could be -1 if the global tab is selected, or the entry doesn't exist if the machine definition is wrong. except KeyError: # Extruder index could be -1 if the global tab is selected, or the entry doesn't exist if the machine definition is wrong.
return None return None
@ -144,7 +144,7 @@ class ExtruderManager(QObject):
@pyqtSlot(result = QObject) @pyqtSlot(result = QObject)
def getActiveExtruderStack(self) -> Optional["ExtruderStack"]: def getActiveExtruderStack(self) -> Optional["ExtruderStack"]:
return self.getExtruderStack(self._active_extruder_index) return self.getExtruderStack(self.activeExtruderIndex)
## Get an extruder stack by index ## Get an extruder stack by index
def getExtruderStack(self, index) -> Optional["ExtruderStack"]: def getExtruderStack(self, index) -> Optional["ExtruderStack"]:

View file

@ -52,8 +52,8 @@ class ExtruderStack(CuraContainerStack):
return super().getNextStack() return super().getNextStack()
def setEnabled(self, enabled: bool) -> None: def setEnabled(self, enabled: bool) -> None:
if "enabled" not in self._metadata: if self.getMetaDataEntry("enabled", True) == enabled: # No change.
self.setMetaDataEntry("enabled", "True") return # Don't emit a signal then.
self.setMetaDataEntry("enabled", str(enabled)) self.setMetaDataEntry("enabled", str(enabled))
self.enabledChanged.emit() self.enabledChanged.emit()

View file

@ -1,4 +1,4 @@
# Copyright (c) 2017 Ultimaker B.V. # Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot, pyqtProperty, QTimer from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot, pyqtProperty, QTimer
@ -24,8 +24,6 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
## Human-readable name of the extruder. ## Human-readable name of the extruder.
NameRole = Qt.UserRole + 2 NameRole = Qt.UserRole + 2
## Is the extruder enabled?
EnabledRole = Qt.UserRole + 9
## Colour of the material loaded in the extruder. ## Colour of the material loaded in the extruder.
ColorRole = Qt.UserRole + 3 ColorRole = Qt.UserRole + 3
@ -47,6 +45,12 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
VariantRole = Qt.UserRole + 7 VariantRole = Qt.UserRole + 7
StackRole = Qt.UserRole + 8 StackRole = Qt.UserRole + 8
MaterialBrandRole = Qt.UserRole + 9
ColorNameRole = Qt.UserRole + 10
## Is the extruder enabled?
EnabledRole = Qt.UserRole + 11
## List of colours to display if there is no material or the material has no known ## List of colours to display if there is no material or the material has no known
# colour. # colour.
defaultColors = ["#ffc924", "#86ec21", "#22eeee", "#245bff", "#9124ff", "#ff24c8"] defaultColors = ["#ffc924", "#86ec21", "#22eeee", "#245bff", "#9124ff", "#ff24c8"]
@ -67,7 +71,8 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
self.addRoleName(self.MaterialRole, "material") self.addRoleName(self.MaterialRole, "material")
self.addRoleName(self.VariantRole, "variant") self.addRoleName(self.VariantRole, "variant")
self.addRoleName(self.StackRole, "stack") self.addRoleName(self.StackRole, "stack")
self.addRoleName(self.MaterialBrandRole, "material_brand")
self.addRoleName(self.ColorNameRole, "color_name")
self._update_extruder_timer = QTimer() self._update_extruder_timer = QTimer()
self._update_extruder_timer.setInterval(100) self._update_extruder_timer.setInterval(100)
self._update_extruder_timer.setSingleShot(True) self._update_extruder_timer.setSingleShot(True)
@ -160,7 +165,7 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
def __updateExtruders(self): def __updateExtruders(self):
extruders_changed = False extruders_changed = False
if self.rowCount() != 0: if self.count != 0:
extruders_changed = True extruders_changed = True
items = [] items = []
@ -172,7 +177,7 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
machine_extruder_count = global_container_stack.getProperty("machine_extruder_count", "value") machine_extruder_count = global_container_stack.getProperty("machine_extruder_count", "value")
for extruder in Application.getInstance().getExtruderManager().getActiveExtruderStacks(): for extruder in Application.getInstance().getExtruderManager().getActiveExtruderStacks():
position = extruder.getMetaDataEntry("position", default = "0") # Get the position position = extruder.getMetaDataEntry("position", default = "0")
try: try:
position = int(position) position = int(position)
except ValueError: except ValueError:
@ -183,7 +188,8 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
default_color = self.defaultColors[position] if 0 <= position < len(self.defaultColors) else self.defaultColors[0] default_color = self.defaultColors[position] if 0 <= position < len(self.defaultColors) else self.defaultColors[0]
color = extruder.material.getMetaDataEntry("color_code", default = default_color) if extruder.material else default_color color = extruder.material.getMetaDataEntry("color_code", default = default_color) if extruder.material else default_color
material_brand = extruder.material.getMetaDataEntry("brand", default = "generic")
color_name = extruder.material.getMetaDataEntry("color_name")
# construct an item with only the relevant information # construct an item with only the relevant information
item = { item = {
"id": extruder.getId(), "id": extruder.getId(),
@ -195,6 +201,8 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
"material": extruder.material.getName() if extruder.material else "", "material": extruder.material.getName() if extruder.material else "",
"variant": extruder.variant.getName() if extruder.variant else "", # e.g. print core "variant": extruder.variant.getName() if extruder.variant else "", # e.g. print core
"stack": extruder, "stack": extruder,
"material_brand": material_brand,
"color_name": color_name
} }
items.append(item) items.append(item)

View file

@ -3,6 +3,8 @@
import collections import collections
import time import time
import re
import unicodedata
from typing import Any, Callable, List, Dict, TYPE_CHECKING, Optional, cast from typing import Any, Callable, List, Dict, TYPE_CHECKING, Optional, cast
from UM.ConfigurationErrorMessage import ConfigurationErrorMessage from UM.ConfigurationErrorMessage import ConfigurationErrorMessage
@ -872,7 +874,7 @@ class MachineManager(QObject):
caution_message = Message(catalog.i18nc( caution_message = Message(catalog.i18nc(
"@info:generic", "@info:generic",
"Settings have been changed to match the current availability of extruders: [%s]" % ", ".join(add_user_changes)), "Settings have been changed to match the current availability of extruders: [%s]" % ", ".join(add_user_changes)),
lifetime=0, lifetime = 0,
title = catalog.i18nc("@info:title", "Settings updated")) title = catalog.i18nc("@info:title", "Settings updated"))
caution_message.show() caution_message.show()
@ -1534,9 +1536,32 @@ class MachineManager(QObject):
name = self._current_quality_group.name name = self._current_quality_group.name
return name return name
@pyqtProperty(bool, notify = activeQualityGroupChanged)
def hasNotSupportedQuality(self) -> bool:
return self._current_quality_group is None and self._current_quality_changes_group is None
def _updateUponMaterialMetadataChange(self) -> None: def _updateUponMaterialMetadataChange(self) -> None:
if self._global_container_stack is None: if self._global_container_stack is None:
return return
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue): with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
self.updateMaterialWithVariant(None) self.updateMaterialWithVariant(None)
self._updateQualityWithMaterial() self._updateQualityWithMaterial()
## This function will translate any printer type name to an abbreviated printer type name
@pyqtSlot(str, result = str)
def getAbbreviatedMachineName(self, machine_type_name: str) -> str:
abbr_machine = ""
for word in re.findall(r"[\w']+", machine_type_name):
if word.lower() == "ultimaker":
abbr_machine += "UM"
elif word.isdigit():
abbr_machine += word
else:
stripped_word = "".join(char for char in unicodedata.normalize("NFD", word.upper()) if unicodedata.category(char) != "Mn")
# - use only the first character if the word is too long (> 3 characters)
# - use the whole word if it's not too long (<= 3 characters)
if len(stripped_word) > 3:
stripped_word = stripped_word[0]
abbr_machine += stripped_word
return abbr_machine

View file

@ -1,23 +1,33 @@
# Copyright (c) 2017 Ultimaker B.V. # Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
from PyQt5.QtCore import pyqtProperty, QUrl from PyQt5.QtCore import pyqtProperty, QUrl
from UM.Stage import Stage from UM.Stage import Stage
# Since Cura has a few pre-defined "space claims" for the locations of certain components, we've provided some structure
# to indicate this.
# * The StageMenuComponent is the horizontal area below the stage bar. This should be used to show stage specific
# buttons and elements. This component will be drawn over the bar & main component.
# * The MainComponent is the component that will be drawn starting from the bottom of the stageBar and fills the rest
# of the screen.
class CuraStage(Stage): class CuraStage(Stage):
def __init__(self, parent = None) -> None:
def __init__(self, parent = None):
super().__init__(parent) super().__init__(parent)
@pyqtProperty(str, constant = True) @pyqtProperty(str, constant = True)
def stageId(self): def stageId(self) -> str:
return self.getPluginId() return self.getPluginId()
@pyqtProperty(QUrl, constant = True) @pyqtProperty(QUrl, constant = True)
def mainComponent(self): def mainComponent(self) -> QUrl:
return self.getDisplayComponent("main") return self.getDisplayComponent("main")
@pyqtProperty(QUrl, constant = True) @pyqtProperty(QUrl, constant = True)
def sidebarComponent(self): def sidebarComponent(self) -> QUrl:
return self.getDisplayComponent("sidebar") return self.getDisplayComponent("sidebar")
@pyqtProperty(QUrl, constant = True)
def stageMenuComponent(self) -> QUrl:
return self.getDisplayComponent("menu")

View file

@ -195,7 +195,7 @@ class ProcessSlicedLayersJob(Job):
if extruders: if extruders:
material_color_map = numpy.zeros((len(extruders), 4), dtype=numpy.float32) material_color_map = numpy.zeros((len(extruders), 4), dtype=numpy.float32)
for extruder in extruders: for extruder in extruders:
position = int(extruder.getMetaDataEntry("position", default="0")) # Get the position position = int(extruder.getMetaDataEntry("position", default = "0"))
try: try:
default_color = ExtrudersModel.defaultColors[position] default_color = ExtrudersModel.defaultColors[position]
except IndexError: except IndexError:

View file

@ -22,7 +22,7 @@ Cura.MachineAction
{ {
id: firmwareUpdaterMachineAction id: firmwareUpdaterMachineAction
anchors.fill: parent; anchors.fill: parent;
UM.I18nCatalog { id: catalog; name:"cura"} UM.I18nCatalog { id: catalog; name: "cura"}
spacing: UM.Theme.getSize("default_margin").height spacing: UM.Theme.getSize("default_margin").height
Label Label

View file

@ -20,7 +20,7 @@ UM.Dialog
GridLayout GridLayout
{ {
UM.I18nCatalog{id: catalog; name:"cura"} UM.I18nCatalog{id: catalog; name: "cura"}
anchors.fill: parent; anchors.fill: parent;
Layout.fillWidth: true Layout.fillWidth: true
columnSpacing: 16 * screenScaleFactor columnSpacing: 16 * screenScaleFactor

View file

@ -1,4 +1,4 @@
// Copyright (c) 2016 Ultimaker B.V. // Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher. // Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.2 import QtQuick 2.2
@ -23,7 +23,7 @@ Cura.MachineAction
target: base.extrudersModel target: base.extrudersModel
onModelChanged: onModelChanged:
{ {
var extruderCount = base.extrudersModel.rowCount(); var extruderCount = base.extrudersModel.count;
base.extruderTabsCount = extruderCount; base.extruderTabsCount = extruderCount;
} }
} }

View file

@ -4,19 +4,19 @@
import QtQuick 2.2 import QtQuick 2.2
import QtQuick.Controls 1.1 import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.1 import QtQuick.Controls.Styles 1.1
import QtQuick.Layouts 1.1
import QtQuick.Dialogs 1.1
import QtQuick.Window 2.2
import UM 1.2 as UM import UM 1.2 as UM
import Cura 1.0 as Cura
Button Button
{ {
id: modelCheckerButton id: modelCheckerButton
UM.I18nCatalog{id: catalog; name:"cura"} UM.I18nCatalog
{
id: catalog
name: "cura"
}
visible: manager.hasWarnings visible: manager.hasWarnings
tooltip: catalog.i18nc("@info:tooltip", "Some things could be problematic in this print. Click to see tips for adjustment.") tooltip: catalog.i18nc("@info:tooltip", "Some things could be problematic in this print. Click to see tips for adjustment.")
@ -25,6 +25,8 @@ Button
width: UM.Theme.getSize("save_button_specs_icons").width width: UM.Theme.getSize("save_button_specs_icons").width
height: UM.Theme.getSize("save_button_specs_icons").height height: UM.Theme.getSize("save_button_specs_icons").height
anchors.verticalCenter: parent ? parent.verticalCenter : undefined
style: ButtonStyle style: ButtonStyle
{ {
background: Item background: Item
@ -33,7 +35,6 @@ Button
{ {
width: UM.Theme.getSize("save_button_specs_icons").width; width: UM.Theme.getSize("save_button_specs_icons").width;
height: UM.Theme.getSize("save_button_specs_icons").height; height: UM.Theme.getSize("save_button_specs_icons").height;
sourceSize.width: width;
sourceSize.height: width; sourceSize.height: width;
color: control.hovered ? UM.Theme.getColor("text_scene_hover") : UM.Theme.getColor("text_scene"); color: control.hovered ? UM.Theme.getColor("text_scene_hover") : UM.Theme.getColor("text_scene");
source: "model_checker.svg" source: "model_checker.svg"

View file

@ -1,45 +1,40 @@
// Copyright (c) 2017 Ultimaker B.V. // Copyright (c) 2017 Ultimaker B.V.
import QtQuick 2.2 import QtQuick 2.10
import QtQuick.Controls 1.1 import QtQuick.Controls 1.4
import UM 1.3 as UM import UM 1.3 as UM
import Cura 1.0 as Cura import Cura 1.0 as Cura
Item
{ Item
// parent could be undefined as this component is not visible at all times {
width: parent ? parent.width : 0 // We show a nice overlay on the 3D viewer when the current output device has no monitor view
height: parent ? parent.height : 0 Rectangle
{
// We show a nice overlay on the 3D viewer when the current output device has no monitor view id: viewportOverlay
Rectangle
{ color: UM.Theme.getColor("viewport_overlay")
id: viewportOverlay anchors.fill: parent
MouseArea
color: UM.Theme.getColor("viewport_overlay") {
width: parent.width anchors.fill: parent
height: parent.height acceptedButtons: Qt.AllButtons
onWheel: wheel.accepted = true
MouseArea }
{ }
anchors.fill: parent
acceptedButtons: Qt.AllButtons Loader
onWheel: wheel.accepted = true {
} id: monitorViewComponent
}
anchors.fill: parent
Loader
{ height: parent.height
id: monitorViewComponent
property real maximumWidth: parent.width
width: parent.width property real maximumHeight: parent.height
height: parent.height
sourceComponent: Cura.MachineManager.printerOutputDevices.length > 0 ? Cura.MachineManager.printerOutputDevices[0].monitorItem: null
property real maximumWidth: parent.width }
property real maximumHeight: parent.height }
sourceComponent: Cura.MachineManager.printerOutputDevices.length > 0 ? Cura.MachineManager.printerOutputDevices[0].monitorItem: null
visible: sourceComponent != null
}
}

View file

@ -0,0 +1,23 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.7
import QtQuick.Controls 2.3
import UM 1.3 as UM
import Cura 1.1 as Cura
Item
{
signal showTooltip(Item item, point location, string text)
signal hideTooltip()
Cura.MachineSelector
{
id: machineSelection
headerCornerSide: Cura.RoundedRectangle.Direction.All
width: UM.Theme.getSize("machine_selector_widget").width
height: parent.height
anchors.centerIn: parent
}
}

View file

@ -65,15 +65,10 @@ class MonitorStage(CuraStage):
# We can only connect now, as we need to be sure that everything is loaded (plugins get created quite early) # We can only connect now, as we need to be sure that everything is loaded (plugins get created quite early)
Application.getInstance().getMachineManager().outputDevicesChanged.connect(self._onOutputDevicesChanged) Application.getInstance().getMachineManager().outputDevicesChanged.connect(self._onOutputDevicesChanged)
self._onOutputDevicesChanged() self._onOutputDevicesChanged()
self._updateMainOverlay()
self._updateSidebar()
def _updateMainOverlay(self): plugin_path = Application.getInstance().getPluginRegistry().getPluginPath(self.getPluginId())
main_component_path = os.path.join(PluginRegistry.getInstance().getPluginPath("MonitorStage"), if plugin_path is not None:
"MonitorMainView.qml") menu_component_path = os.path.join(plugin_path, "MonitorMenu.qml")
self.addDisplayComponent("main", main_component_path) main_component_path = os.path.join(plugin_path, "MonitorMain.qml")
self.addDisplayComponent("menu", menu_component_path)
def _updateSidebar(self): self.addDisplayComponent("main", main_component_path)
sidebar_component_path = os.path.join(Resources.getPath(Application.getInstance().ResourceTypes.QmlFiles),
"MonitorSidebar.qml")
self.addDisplayComponent("sidebar", sidebar_component_path)

View file

@ -7,14 +7,16 @@ from . import MonitorStage
from UM.i18n import i18nCatalog from UM.i18n import i18nCatalog
i18n_catalog = i18nCatalog("cura") i18n_catalog = i18nCatalog("cura")
def getMetaData(): def getMetaData():
return { return {
"stage": { "stage": {
"name": i18n_catalog.i18nc("@item:inmenu", "Monitor"), "name": i18n_catalog.i18nc("@item:inmenu", "Monitor"),
"weight": 1 "weight": 2
} }
} }
def register(app): def register(app):
return { return {
"stage": MonitorStage.MonitorStage() "stage": MonitorStage.MonitorStage()

View file

@ -265,7 +265,6 @@ Item {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
width: parent.width width: parent.width
height: width height: width
sourceSize.width: width
sourceSize.height: width sourceSize.height: width
color: control.hovered ? UM.Theme.getColor("setting_control_button_hover") : UM.Theme.getColor("setting_control_button") color: control.hovered ? UM.Theme.getColor("setting_control_button_hover") : UM.Theme.getColor("setting_control_button")
source: UM.Theme.getIcon("minus") source: UM.Theme.getIcon("minus")

View file

@ -31,7 +31,7 @@ UM.Dialog
Item Item
{ {
UM.I18nCatalog{id: catalog; name:"cura"} UM.I18nCatalog{id: catalog; name: "cura"}
id: base id: base
property int columnWidth: Math.round((base.width / 2) - UM.Theme.getSize("default_margin").width) property int columnWidth: Math.round((base.width / 2) - UM.Theme.getSize("default_margin").width)
property int textMargin: Math.round(UM.Theme.getSize("default_margin").width / 2) property int textMargin: Math.round(UM.Theme.getSize("default_margin").width / 2)
@ -141,7 +141,6 @@ UM.Dialog
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
width: Math.round(control.width / 2.7) width: Math.round(control.width / 2.7)
height: Math.round(control.height / 2.7) height: Math.round(control.height / 2.7)
sourceSize.width: width
sourceSize.height: width sourceSize.height: width
color: palette.text color: palette.text
source: UM.Theme.getIcon("cross1") source: UM.Theme.getIcon("cross1")
@ -176,7 +175,6 @@ UM.Dialog
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
width: Math.round(control.width / 2.5) width: Math.round(control.width / 2.5)
height: Math.round(control.height / 2.5) height: Math.round(control.height / 2.5)
sourceSize.width: width
sourceSize.height: width sourceSize.height: width
color: control.enabled ? palette.text : disabledPalette.text color: control.enabled ? palette.text : disabledPalette.text
source: UM.Theme.getIcon("arrow_bottom") source: UM.Theme.getIcon("arrow_bottom")
@ -211,7 +209,6 @@ UM.Dialog
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
width: Math.round(control.width / 2.5) width: Math.round(control.width / 2.5)
height: Math.round(control.height / 2.5) height: Math.round(control.height / 2.5)
sourceSize.width: width
sourceSize.height: width sourceSize.height: width
color: control.enabled ? palette.text : disabledPalette.text color: control.enabled ? palette.text : disabledPalette.text
source: UM.Theme.getIcon("arrow_top") source: UM.Theme.getIcon("arrow_top")
@ -260,7 +257,7 @@ UM.Dialog
Rectangle Rectangle
{ {
color: UM.Theme.getColor("sidebar") color: UM.Theme.getColor("main_background")
anchors.left: activeScripts.right anchors.left: activeScripts.right
anchors.leftMargin: UM.Theme.getSize("default_margin").width anchors.leftMargin: UM.Theme.getSize("default_margin").width
anchors.right: parent.right anchors.right: parent.right
@ -415,7 +412,7 @@ UM.Dialog
} }
} }
Cura.SidebarTooltip Cura.PrintSetupTooltip
{ {
id: tooltip id: tooltip
} }
@ -498,7 +495,6 @@ UM.Dialog
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
width: Math.round(parent.width / 2) width: Math.round(parent.width / 2)
height: Math.round(parent.height / 2) height: Math.round(parent.height / 2)
sourceSize.width: width
sourceSize.height: height sourceSize.height: height
color: !control.enabled ? UM.Theme.getColor("action_button_disabled_text") : color: !control.enabled ? UM.Theme.getColor("action_button_disabled_text") :
control.pressed ? UM.Theme.getColor("action_button_active_text") : control.pressed ? UM.Theme.getColor("action_button_active_text") :

View file

@ -0,0 +1,134 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.7
import QtQuick.Layouts 1.1
import QtQuick.Controls 2.3
import UM 1.3 as UM
import Cura 1.1 as Cura
import QtGraphicalEffects 1.0 // For the dropshadow
Item
{
id: prepareMenu
UM.I18nCatalog
{
id: catalog
name: "cura"
}
// Item to ensure that all of the buttons are nicely centered.
Item
{
anchors.horizontalCenter: parent.horizontalCenter
width: openFileButton.width + itemRow.width + UM.Theme.getSize("default_margin").width
height: parent.height
RowLayout
{
id: itemRow
anchors.left: openFileButton.right
anchors.leftMargin: UM.Theme.getSize("default_margin").width
width: Math.round(0.9 * prepareMenu.width)
height: parent.height
spacing: 0
Cura.MachineSelector
{
id: machineSelection
headerCornerSide: Cura.RoundedRectangle.Direction.Left
Layout.minimumWidth: UM.Theme.getSize("machine_selector_widget").width
Layout.maximumWidth: UM.Theme.getSize("machine_selector_widget").width
Layout.fillWidth: true
Layout.fillHeight: true
}
// Separator line
Rectangle
{
height: parent.height
width: UM.Theme.getSize("default_lining").width
color: UM.Theme.getColor("lining")
}
Cura.ConfigurationMenu
{
Layout.fillHeight: true
Layout.fillWidth: true
Layout.preferredWidth: itemRow.width - machineSelection.width - printSetupSelectorItem.width - 2 * UM.Theme.getSize("default_lining").width
}
// Separator line
Rectangle
{
height: parent.height
width: UM.Theme.getSize("default_lining").width
color: UM.Theme.getColor("lining")
}
Item
{
id: printSetupSelectorItem
// This is a work around to prevent the printSetupSelector from having to be re-loaded every time
// a stage switch is done.
children: [printSetupSelector]
height: childrenRect.height
width: childrenRect.width
}
}
Button
{
id: openFileButton
height: UM.Theme.getSize("stage_menu").height
width: UM.Theme.getSize("stage_menu").height
onClicked: Cura.Actions.open.trigger()
hoverEnabled: true
contentItem: Item
{
anchors.fill: parent
UM.RecolorImage
{
id: buttonIcon
anchors.centerIn: parent
source: UM.Theme.getIcon("load")
width: UM.Theme.getSize("button_icon").width
height: UM.Theme.getSize("button_icon").height
color: UM.Theme.getColor("toolbar_button_text")
sourceSize.height: height
}
}
background: Rectangle
{
id: background
height: UM.Theme.getSize("stage_menu").height
width: UM.Theme.getSize("stage_menu").height
radius: UM.Theme.getSize("default_radius").width
color: openFileButton.hovered ? UM.Theme.getColor("action_button_hovered") : UM.Theme.getColor("action_button")
}
DropShadow
{
id: shadow
// Don't blur the shadow
radius: 0
anchors.fill: background
source: background
verticalOffset: 2
visible: true
color: UM.Theme.getColor("action_button_shadow")
// Should always be drawn behind the background.
z: background.z - 1
}
}
}
}

View file

@ -2,13 +2,14 @@
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
import os.path import os.path
from UM.Application import Application from UM.Application import Application
from UM.PluginRegistry import PluginRegistry
from UM.Resources import Resources from UM.Resources import Resources
from cura.Stages.CuraStage import CuraStage from cura.Stages.CuraStage import CuraStage
## Stage for preparing model (slicing). ## Stage for preparing model (slicing).
class PrepareStage(CuraStage): class PrepareStage(CuraStage):
def __init__(self, parent = None): def __init__(self, parent = None):
super().__init__(parent) super().__init__(parent)
Application.getInstance().engineCreatedSignal.connect(self._engineCreated) Application.getInstance().engineCreatedSignal.connect(self._engineCreated)
@ -16,4 +17,7 @@ class PrepareStage(CuraStage):
def _engineCreated(self): def _engineCreated(self):
sidebar_component_path = os.path.join(Resources.getPath(Application.getInstance().ResourceTypes.QmlFiles), sidebar_component_path = os.path.join(Resources.getPath(Application.getInstance().ResourceTypes.QmlFiles),
"PrepareSidebar.qml") "PrepareSidebar.qml")
menu_component_path = os.path.join(PluginRegistry.getInstance().getPluginPath("PrepareStage"), "PrepareMenu.qml")
self.addDisplayComponent("menu", menu_component_path)
self.addDisplayComponent("sidebar", sidebar_component_path) self.addDisplayComponent("sidebar", sidebar_component_path)

View file

@ -0,0 +1,18 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.4
import QtQuick.Controls 1.2
import QtQuick.Layouts 1.1
import QtQuick.Controls.Styles 1.1
import UM 1.0 as UM
import Cura 1.0 as Cura
Loader
{
id: previewMain
source: UM.Controller.activeView != null && UM.Controller.activeView.mainComponent != null ? UM.Controller.activeView.mainComponent : ""
}

View file

@ -0,0 +1,74 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.7
import QtQuick.Controls 2.3
import UM 1.3 as UM
import Cura 1.1 as Cura
Item
{
id: previewMenu
property real itemHeight: height - 2 * UM.Theme.getSize("default_lining").width
UM.I18nCatalog
{
id: catalog
name: "cura"
}
Row
{
id: stageMenuRow
anchors.centerIn: parent
height: parent.height
Cura.ViewsSelector
{
id: viewsSelector
height: parent.height
width: UM.Theme.getSize("views_selector").width
headerCornerSide: Cura.RoundedRectangle.Direction.Left
}
// Separator line
Rectangle
{
height: parent.height
// If there is no viewPanel, we only need a single spacer, so hide this one.
visible: viewPanel.source != ""
width: visible ? UM.Theme.getSize("default_lining").width : 0
color: UM.Theme.getColor("lining")
}
Loader
{
id: viewPanel
height: parent.height
width: childrenRect.width
source: UM.Controller.activeView != null && UM.Controller.activeView.stageMenuComponent != null ? UM.Controller.activeView.stageMenuComponent : ""
}
// Separator line
Rectangle
{
height: parent.height
width: UM.Theme.getSize("default_lining").width
color: UM.Theme.getColor("lining")
}
Item
{
id: printSetupSelectorItem
// This is a work around to prevent the printSetupSelector from having to be re-loaded every time
// a stage switch is done.
children: [printSetupSelector]
height: childrenRect.height
width: childrenRect.width
}
}
}

View file

@ -0,0 +1,51 @@
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import os.path
from UM.Qt.QtApplication import QtApplication
from cura.Stages.CuraStage import CuraStage
from typing import TYPE_CHECKING, Optional
if TYPE_CHECKING:
from UM.View.View import View
## Displays a preview of what you're about to print.
#
# The Python component of this stage just loads PreviewMain.qml for display
# when the stage is selected, and makes sure that it reverts to the previous
# view when the previous stage is activated.
class PreviewStage(CuraStage):
def __init__(self, application: QtApplication, parent = None) -> None:
super().__init__(parent)
self._application = application
self._application.engineCreatedSignal.connect(self._engineCreated)
self._previously_active_view = None # type: Optional[View]
## When selecting the stage, remember which was the previous view so that
# we can revert to that view when we go out of the stage later.
def onStageSelected(self) -> None:
self._previously_active_view = self._application.getController().getActiveView()
## Called when going to a different stage (away from the Preview Stage).
#
# When going to a different stage, the view should be reverted to what it
# was before. Normally, that just reverts it to solid view.
def onStageDeselected(self) -> None:
if self._previously_active_view is not None:
self._application.getController().setActiveView(self._previously_active_view.getPluginId())
self._previously_active_view = None
## Delayed load of the QML files.
#
# We need to make sure that the QML engine is running before we can load
# these.
def _engineCreated(self) -> None:
plugin_path = self._application.getPluginRegistry().getPluginPath(self.getPluginId())
if plugin_path is not None:
menu_component_path = os.path.join(plugin_path, "PreviewMenu.qml")
main_component_path = os.path.join(plugin_path, "PreviewMain.qml")
self.addDisplayComponent("menu", menu_component_path)
self.addDisplayComponent("main", main_component_path)

View file

@ -0,0 +1,22 @@
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from . import PreviewStage
from UM.i18n import i18nCatalog
i18n_catalog = i18nCatalog("cura")
def getMetaData():
return {
"stage": {
"name": i18n_catalog.i18nc("@item:inmenu", "Preview"),
"weight": 1
}
}
def register(app):
return {
"stage": PreviewStage.PreviewStage(app)
}

View file

@ -0,0 +1,8 @@
{
"name": "Preview Stage",
"author": "Ultimaker B.V.",
"version": "1.0.0",
"description": "Provides a preview stage in Cura.",
"api": 5,
"i18n-catalog": "cura"
}

View file

@ -13,23 +13,20 @@ Item
{ {
id: sliderRoot id: sliderRoot
// handle properties // Handle properties
property real handleSize: 10 property real handleSize: UM.Theme.getSize("slider_handle").width
property real handleRadius: handleSize / 2 property real handleRadius: handleSize / 2
property real minimumRangeHandleSize: handleSize / 2 property real minimumRangeHandleSize: handleSize / 2
property color upperHandleColor: "black" property color upperHandleColor: UM.Theme.getColor("slider_handle")
property color lowerHandleColor: "black" property color lowerHandleColor: UM.Theme.getColor("slider_handle")
property color rangeHandleColor: "black" property color rangeHandleColor: UM.Theme.getColor("slider_groove_fill")
property color handleActiveColor: "white" property color handleActiveColor: UM.Theme.getColor("slider_handle_active")
property real handleLabelWidth: width
property var activeHandle: upperHandle property var activeHandle: upperHandle
// track properties // Track properties
property real trackThickness: 4 // width of the slider track property real trackThickness: UM.Theme.getSize("slider_groove").width // width of the slider track
property real trackRadius: trackThickness / 2 property real trackRadius: UM.Theme.getSize("slider_groove_radius").width
property color trackColor: "white" property color trackColor: UM.Theme.getColor("slider_groove")
property real trackBorderWidth: 1 // width of the slider track border
property color trackBorderColor: "black"
// value properties // value properties
property real maximumValue: 100 property real maximumValue: 100
@ -80,7 +77,7 @@ Item
return Math.min(Math.max(value, sliderRoot.minimumValue), sliderRoot.maximumValue) return Math.min(Math.max(value, sliderRoot.minimumValue), sliderRoot.maximumValue)
} }
// slider track // Slider track
Rectangle Rectangle
{ {
id: track id: track
@ -90,8 +87,6 @@ Item
radius: sliderRoot.trackRadius radius: sliderRoot.trackRadius
anchors.centerIn: sliderRoot anchors.centerIn: sliderRoot
color: sliderRoot.trackColor color: sliderRoot.trackColor
border.width: sliderRoot.trackBorderWidth
border.color: sliderRoot.trackBorderColor
visible: sliderRoot.layersVisible visible: sliderRoot.layersVisible
} }
@ -106,7 +101,7 @@ Item
anchors.horizontalCenter: sliderRoot.horizontalCenter anchors.horizontalCenter: sliderRoot.horizontalCenter
visible: sliderRoot.layersVisible visible: sliderRoot.layersVisible
// set the new value when dragging // Set the new value when dragging
function onHandleDragged() function onHandleDragged()
{ {
sliderRoot.manuallyChanged = true sliderRoot.manuallyChanged = true
@ -140,9 +135,10 @@ Item
Rectangle Rectangle
{ {
width: sliderRoot.trackThickness - 2 * sliderRoot.trackBorderWidth width: sliderRoot.trackThickness
height: parent.height + sliderRoot.handleSize height: parent.height + sliderRoot.handleSize
anchors.centerIn: parent anchors.centerIn: parent
radius: sliderRoot.trackRadius
color: sliderRoot.rangeHandleColor color: sliderRoot.rangeHandleColor
} }
@ -167,9 +163,9 @@ Item
id: rangleHandleLabel id: rangleHandleLabel
height: sliderRoot.handleSize + UM.Theme.getSize("default_margin").height height: sliderRoot.handleSize + UM.Theme.getSize("default_margin").height
x: parent.x - width - UM.Theme.getSize("default_margin").width x: parent.x + parent.width + UM.Theme.getSize("default_margin").width
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
target: Qt.point(sliderRoot.width, y + height / 2) target: Qt.point(sliderRoot.width + width, y + height / 2)
visible: sliderRoot.activeHandle == parent visible: sliderRoot.activeHandle == parent
// custom properties // custom properties
@ -275,7 +271,7 @@ Item
id: upperHandleLabel id: upperHandleLabel
height: sliderRoot.handleSize + UM.Theme.getSize("default_margin").height height: sliderRoot.handleSize + UM.Theme.getSize("default_margin").height
x: parent.x - width - UM.Theme.getSize("default_margin").width x: parent.x - parent.width - width
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
target: Qt.point(sliderRoot.width, y + height / 2) target: Qt.point(sliderRoot.width, y + height / 2)
visible: sliderRoot.activeHandle == parent visible: sliderRoot.activeHandle == parent
@ -385,9 +381,9 @@ Item
id: lowerHandleLabel id: lowerHandleLabel
height: sliderRoot.handleSize + UM.Theme.getSize("default_margin").height height: sliderRoot.handleSize + UM.Theme.getSize("default_margin").height
x: parent.x - width - UM.Theme.getSize("default_margin").width x: parent.x - parent.width - width
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
target: Qt.point(sliderRoot.width, y + height / 2) target: Qt.point(sliderRoot.width + width, y + height / 2)
visible: sliderRoot.activeHandle == parent visible: sliderRoot.activeHandle == parent
// custom properties // custom properties

View file

@ -14,19 +14,17 @@ Item
id: sliderRoot id: sliderRoot
// handle properties // handle properties
property real handleSize: 10 property real handleSize: UM.Theme.getSize("slider_handle").width
property real handleRadius: handleSize / 2 property real handleRadius: handleSize / 2
property color handleColor: "black" property color handleColor: UM.Theme.getColor("slider_handle")
property color handleActiveColor: "white" property color handleActiveColor: UM.Theme.getColor("slider_handle_active")
property color rangeColor: "black" property color rangeColor: UM.Theme.getColor("slider_groove_fill")
property real handleLabelWidth: width property real handleLabelWidth: width
// track properties // track properties
property real trackThickness: 4 // width of the slider track property real trackThickness: UM.Theme.getSize("slider_groove").width
property real trackRadius: trackThickness / 2 property real trackRadius: UM.Theme.getSize("slider_groove_radius").width
property color trackColor: "white" property color trackColor: UM.Theme.getColor("slider_groove")
property real trackBorderWidth: 1 // width of the slider track border
property color trackBorderColor: "black"
// value properties // value properties
property real maximumValue: 100 property real maximumValue: 100
@ -68,8 +66,6 @@ Item
radius: sliderRoot.trackRadius radius: sliderRoot.trackRadius
anchors.centerIn: sliderRoot anchors.centerIn: sliderRoot
color: sliderRoot.trackColor color: sliderRoot.trackColor
border.width: sliderRoot.trackBorderWidth
border.color: sliderRoot.trackBorderColor
visible: sliderRoot.pathsVisible visible: sliderRoot.pathsVisible
} }
@ -86,9 +82,10 @@ Item
Rectangle Rectangle
{ {
height: sliderRoot.trackThickness - 2 * sliderRoot.trackBorderWidth height: sliderRoot.trackThickness
width: parent.width + sliderRoot.handleSize width: parent.width + sliderRoot.handleSize
anchors.centerIn: parent anchors.centerIn: parent
radius: sliderRoot.trackRadius
color: sliderRoot.rangeColor color: sliderRoot.rangeColor
} }
} }

View file

@ -16,6 +16,7 @@ from UM.Mesh.MeshBuilder import MeshBuilder
from UM.Message import Message from UM.Message import Message
from UM.Platform import Platform from UM.Platform import Platform
from UM.PluginRegistry import PluginRegistry from UM.PluginRegistry import PluginRegistry
from UM.Qt.QtApplication import QtApplication
from UM.Resources import Resources from UM.Resources import Resources
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
@ -26,8 +27,8 @@ from UM.View.GL.OpenGL import OpenGL
from UM.View.GL.OpenGLContext import OpenGLContext from UM.View.GL.OpenGLContext import OpenGLContext
from UM.View.GL.ShaderProgram import ShaderProgram from UM.View.GL.ShaderProgram import ShaderProgram
from UM.View.View import View
from UM.i18n import i18nCatalog from UM.i18n import i18nCatalog
from cura.CuraView import CuraView
from cura.Scene.ConvexHullNode import ConvexHullNode from cura.Scene.ConvexHullNode import ConvexHullNode
from cura.CuraApplication import CuraApplication from cura.CuraApplication import CuraApplication
@ -48,15 +49,15 @@ catalog = i18nCatalog("cura")
## View used to display g-code paths. ## View used to display g-code paths.
class SimulationView(View): class SimulationView(CuraView):
# Must match SimulationView.qml # Must match SimulationView.qml
LAYER_VIEW_TYPE_MATERIAL_TYPE = 0 LAYER_VIEW_TYPE_MATERIAL_TYPE = 0
LAYER_VIEW_TYPE_LINE_TYPE = 1 LAYER_VIEW_TYPE_LINE_TYPE = 1
LAYER_VIEW_TYPE_FEEDRATE = 2 LAYER_VIEW_TYPE_FEEDRATE = 2
LAYER_VIEW_TYPE_THICKNESS = 3 LAYER_VIEW_TYPE_THICKNESS = 3
def __init__(self) -> None: def __init__(self, parent = None) -> None:
super().__init__() super().__init__(parent)
self._max_layers = 0 self._max_layers = 0
self._current_layer_num = 0 self._current_layer_num = 0
@ -113,6 +114,16 @@ class SimulationView(View):
self._wireprint_warning_message = Message(catalog.i18nc("@info:status", "Cura does not accurately display layers when Wire Printing is enabled"), self._wireprint_warning_message = Message(catalog.i18nc("@info:status", "Cura does not accurately display layers when Wire Printing is enabled"),
title = catalog.i18nc("@info:title", "Simulation View")) title = catalog.i18nc("@info:title", "Simulation View"))
QtApplication.getInstance().engineCreatedSignal.connect(self._onEngineCreated)
def _onEngineCreated(self) -> None:
plugin_path = PluginRegistry.getInstance().getPluginPath(self.getPluginId())
if plugin_path:
self.addDisplayComponent("main", os.path.join(plugin_path, "SimulationViewMainComponent.qml"))
self.addDisplayComponent("menu", os.path.join(plugin_path, "SimulationViewMenuComponent.qml"))
else:
Logger.log("e", "Unable to find the path for %s", self.getPluginId())
def _evaluateCompatibilityMode(self) -> bool: def _evaluateCompatibilityMode(self) -> bool:
return OpenGLContext.isLegacyOpenGL() or bool(Application.getInstance().getPreferences().getValue("view/force_layer_view_compatibility_mode")) return OpenGLContext.isLegacyOpenGL() or bool(Application.getInstance().getPreferences().getValue("view/force_layer_view_compatibility_mode"))

View file

@ -1,808 +0,0 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.4
import QtQuick.Controls 1.2
import QtQuick.Layouts 1.1
import QtQuick.Controls.Styles 1.1
import UM 1.0 as UM
import Cura 1.0 as Cura
Item
{
id: base
width:
{
if (UM.SimulationView.compatibilityMode)
{
return UM.Theme.getSize("layerview_menu_size_compatibility").width;
}
else
{
return UM.Theme.getSize("layerview_menu_size").width;
}
}
height: {
if (viewSettings.collapsed)
{
if (UM.SimulationView.compatibilityMode)
{
return UM.Theme.getSize("layerview_menu_size_compatibility_collapsed").height;
}
return UM.Theme.getSize("layerview_menu_size_collapsed").height;
}
else if (UM.SimulationView.compatibilityMode)
{
return UM.Theme.getSize("layerview_menu_size_compatibility").height;
}
else if (UM.Preferences.getValue("layerview/layer_view_type") == 0)
{
return UM.Theme.getSize("layerview_menu_size_material_color_mode").height + UM.SimulationView.extruderCount * (UM.Theme.getSize("layerview_row").height + UM.Theme.getSize("layerview_row_spacing").height)
}
else
{
return UM.Theme.getSize("layerview_menu_size").height + UM.SimulationView.extruderCount * (UM.Theme.getSize("layerview_row").height + UM.Theme.getSize("layerview_row_spacing").height)
}
}
Behavior on height { NumberAnimation { duration: 100 } }
property var buttonTarget:
{
if(parent != null)
{
var force_binding = parent.y; // ensure this gets reevaluated when the panel moves
return base.mapFromItem(parent.parent, parent.buttonTarget.x, parent.buttonTarget.y)
}
return Qt.point(0,0)
}
Rectangle
{
id: layerViewMenu
anchors.right: parent.right
anchors.top: parent.top
width: parent.width
height: parent.height
clip: true
z: layerSlider.z - 1
color: UM.Theme.getColor("tool_panel_background")
border.width: UM.Theme.getSize("default_lining").width
border.color: UM.Theme.getColor("lining")
Button {
id: collapseButton
anchors.top: parent.top
anchors.topMargin: Math.round(UM.Theme.getSize("default_margin").height + (UM.Theme.getSize("layerview_row").height - UM.Theme.getSize("default_margin").height) / 2)
anchors.right: parent.right
anchors.rightMargin: UM.Theme.getSize("default_margin").width
width: UM.Theme.getSize("standard_arrow").width
height: UM.Theme.getSize("standard_arrow").height
onClicked: viewSettings.collapsed = !viewSettings.collapsed
style: ButtonStyle
{
background: UM.RecolorImage
{
width: control.width
height: control.height
sourceSize.width: width
sourceSize.height: width
color: UM.Theme.getColor("setting_control_text")
source: viewSettings.collapsed ? UM.Theme.getIcon("arrow_left") : UM.Theme.getIcon("arrow_bottom")
}
label: Label{ }
}
}
ColumnLayout
{
id: viewSettings
property bool collapsed: false
property var extruder_opacities: UM.Preferences.getValue("layerview/extruder_opacities").split("|")
property bool show_travel_moves: UM.Preferences.getValue("layerview/show_travel_moves")
property bool show_helpers: UM.Preferences.getValue("layerview/show_helpers")
property bool show_skin: UM.Preferences.getValue("layerview/show_skin")
property bool show_infill: UM.Preferences.getValue("layerview/show_infill")
// if we are in compatibility mode, we only show the "line type"
property bool show_legend: UM.SimulationView.compatibilityMode ? true : UM.Preferences.getValue("layerview/layer_view_type") == 1
property bool show_gradient: UM.SimulationView.compatibilityMode ? false : UM.Preferences.getValue("layerview/layer_view_type") == 2 || UM.Preferences.getValue("layerview/layer_view_type") == 3
property bool show_feedrate_gradient: show_gradient && UM.Preferences.getValue("layerview/layer_view_type") == 2
property bool show_thickness_gradient: show_gradient && UM.Preferences.getValue("layerview/layer_view_type") == 3
property bool only_show_top_layers: UM.Preferences.getValue("view/only_show_top_layers")
property int top_layer_count: UM.Preferences.getValue("view/top_layer_count")
anchors.top: parent.top
anchors.topMargin: UM.Theme.getSize("default_margin").height
anchors.left: parent.left
anchors.leftMargin: UM.Theme.getSize("default_margin").width
anchors.right: parent.right
anchors.rightMargin: UM.Theme.getSize("default_margin").width
spacing: UM.Theme.getSize("layerview_row_spacing").height
Label
{
id: layerViewTypesLabel
anchors.left: parent.left
text: catalog.i18nc("@label","Color scheme")
font: UM.Theme.getFont("default");
visible: !UM.SimulationView.compatibilityMode
Layout.fillWidth: true
color: UM.Theme.getColor("setting_control_text")
}
ListModel // matches SimulationView.py
{
id: layerViewTypes
}
Component.onCompleted:
{
layerViewTypes.append({
text: catalog.i18nc("@label:listbox", "Material Color"),
type_id: 0
})
layerViewTypes.append({
text: catalog.i18nc("@label:listbox", "Line Type"),
type_id: 1
})
layerViewTypes.append({
text: catalog.i18nc("@label:listbox", "Feedrate"),
type_id: 2
})
layerViewTypes.append({
text: catalog.i18nc("@label:listbox", "Layer thickness"),
type_id: 3 // these ids match the switching in the shader
})
}
ComboBox
{
id: layerTypeCombobox
anchors.left: parent.left
Layout.fillWidth: true
Layout.preferredWidth: UM.Theme.getSize("layerview_row").width
model: layerViewTypes
visible: !UM.SimulationView.compatibilityMode
style: UM.Theme.styles.combobox
anchors.right: parent.right
onActivated:
{
UM.Preferences.setValue("layerview/layer_view_type", index);
}
Component.onCompleted:
{
currentIndex = UM.SimulationView.compatibilityMode ? 1 : UM.Preferences.getValue("layerview/layer_view_type");
updateLegends(currentIndex);
}
function updateLegends(type_id)
{
// update visibility of legends
viewSettings.show_legend = UM.SimulationView.compatibilityMode || (type_id == 1);
viewSettings.show_gradient = !UM.SimulationView.compatibilityMode && (type_id == 2 || type_id == 3);
viewSettings.show_feedrate_gradient = viewSettings.show_gradient && (type_id == 2);
viewSettings.show_thickness_gradient = viewSettings.show_gradient && (type_id == 3);
}
}
Label
{
id: compatibilityModeLabel
anchors.left: parent.left
text: catalog.i18nc("@label","Compatibility Mode")
font: UM.Theme.getFont("default")
color: UM.Theme.getColor("text")
visible: UM.SimulationView.compatibilityMode
Layout.fillWidth: true
Layout.preferredHeight: UM.Theme.getSize("layerview_row").height
Layout.preferredWidth: UM.Theme.getSize("layerview_row").width
}
Item
{
height: Math.round(UM.Theme.getSize("default_margin").width / 2)
width: width
}
Connections
{
target: UM.Preferences
onPreferenceChanged:
{
layerTypeCombobox.currentIndex = UM.SimulationView.compatibilityMode ? 1 : UM.Preferences.getValue("layerview/layer_view_type");
layerTypeCombobox.updateLegends(layerTypeCombobox.currentIndex);
playButton.pauseSimulation();
viewSettings.extruder_opacities = UM.Preferences.getValue("layerview/extruder_opacities").split("|");
viewSettings.show_travel_moves = UM.Preferences.getValue("layerview/show_travel_moves");
viewSettings.show_helpers = UM.Preferences.getValue("layerview/show_helpers");
viewSettings.show_skin = UM.Preferences.getValue("layerview/show_skin");
viewSettings.show_infill = UM.Preferences.getValue("layerview/show_infill");
viewSettings.only_show_top_layers = UM.Preferences.getValue("view/only_show_top_layers");
viewSettings.top_layer_count = UM.Preferences.getValue("view/top_layer_count");
}
}
Repeater
{
model: Cura.ExtrudersModel{}
CheckBox
{
id: extrudersModelCheckBox
checked: viewSettings.extruder_opacities[index] > 0.5 || viewSettings.extruder_opacities[index] == undefined || viewSettings.extruder_opacities[index] == ""
onClicked:
{
viewSettings.extruder_opacities[index] = checked ? 1.0 : 0.0
UM.Preferences.setValue("layerview/extruder_opacities", viewSettings.extruder_opacities.join("|"));
}
visible: !UM.SimulationView.compatibilityMode
enabled: index + 1 <= 4
Rectangle
{
anchors.verticalCenter: parent.verticalCenter
anchors.right: extrudersModelCheckBox.right
width: UM.Theme.getSize("layerview_legend_size").width
height: UM.Theme.getSize("layerview_legend_size").height
color: model.color
radius: Math.round(width / 2)
border.width: UM.Theme.getSize("default_lining").width
border.color: UM.Theme.getColor("lining")
visible: !viewSettings.show_legend & !viewSettings.show_gradient
}
Layout.fillWidth: true
Layout.preferredHeight: UM.Theme.getSize("layerview_row").height + UM.Theme.getSize("default_lining").height
Layout.preferredWidth: UM.Theme.getSize("layerview_row").width
style: UM.Theme.styles.checkbox
Label
{
text: model.name
elide: Text.ElideRight
color: UM.Theme.getColor("setting_control_text")
font: UM.Theme.getFont("default")
anchors.verticalCenter: parent.verticalCenter
anchors.left: extrudersModelCheckBox.left;
anchors.right: extrudersModelCheckBox.right;
anchors.leftMargin: UM.Theme.getSize("checkbox").width + Math.round(UM.Theme.getSize("default_margin").width/2)
anchors.rightMargin: UM.Theme.getSize("default_margin").width * 2
}
}
}
Repeater
{
model: ListModel
{
id: typesLegendModel
Component.onCompleted:
{
typesLegendModel.append({
label: catalog.i18nc("@label", "Show Travels"),
initialValue: viewSettings.show_travel_moves,
preference: "layerview/show_travel_moves",
colorId: "layerview_move_combing"
});
typesLegendModel.append({
label: catalog.i18nc("@label", "Show Helpers"),
initialValue: viewSettings.show_helpers,
preference: "layerview/show_helpers",
colorId: "layerview_support"
});
typesLegendModel.append({
label: catalog.i18nc("@label", "Show Shell"),
initialValue: viewSettings.show_skin,
preference: "layerview/show_skin",
colorId: "layerview_inset_0"
});
typesLegendModel.append({
label: catalog.i18nc("@label", "Show Infill"),
initialValue: viewSettings.show_infill,
preference: "layerview/show_infill",
colorId: "layerview_infill"
});
}
}
CheckBox
{
id: legendModelCheckBox
checked: model.initialValue
onClicked:
{
UM.Preferences.setValue(model.preference, checked);
}
Rectangle
{
anchors.verticalCenter: parent.verticalCenter
anchors.right: legendModelCheckBox.right
width: UM.Theme.getSize("layerview_legend_size").width
height: UM.Theme.getSize("layerview_legend_size").height
color: UM.Theme.getColor(model.colorId)
border.width: UM.Theme.getSize("default_lining").width
border.color: UM.Theme.getColor("lining")
visible: viewSettings.show_legend
}
Layout.fillWidth: true
Layout.preferredHeight: UM.Theme.getSize("layerview_row").height + UM.Theme.getSize("default_lining").height
Layout.preferredWidth: UM.Theme.getSize("layerview_row").width
style: UM.Theme.styles.checkbox
Label
{
text: label
font: UM.Theme.getFont("default")
elide: Text.ElideRight
color: UM.Theme.getColor("setting_control_text")
anchors.verticalCenter: parent.verticalCenter
anchors.left: legendModelCheckBox.left;
anchors.right: legendModelCheckBox.right;
anchors.leftMargin: UM.Theme.getSize("checkbox").width + Math.round(UM.Theme.getSize("default_margin").width/2)
anchors.rightMargin: UM.Theme.getSize("default_margin").width * 2
}
}
}
CheckBox
{
checked: viewSettings.only_show_top_layers
onClicked:
{
UM.Preferences.setValue("view/only_show_top_layers", checked ? 1.0 : 0.0);
}
text: catalog.i18nc("@label", "Only Show Top Layers")
visible: UM.SimulationView.compatibilityMode
style: UM.Theme.styles.checkbox
}
CheckBox
{
checked: viewSettings.top_layer_count == 5
onClicked:
{
UM.Preferences.setValue("view/top_layer_count", checked ? 5 : 1);
}
text: catalog.i18nc("@label", "Show 5 Detailed Layers On Top")
visible: UM.SimulationView.compatibilityMode
style: UM.Theme.styles.checkbox
}
Repeater
{
model: ListModel
{
id: typesLegendModelNoCheck
Component.onCompleted:
{
typesLegendModelNoCheck.append({
label: catalog.i18nc("@label", "Top / Bottom"),
colorId: "layerview_skin",
});
typesLegendModelNoCheck.append({
label: catalog.i18nc("@label", "Inner Wall"),
colorId: "layerview_inset_x",
});
}
}
Label
{
text: label
visible: viewSettings.show_legend
id: typesLegendModelLabel
Rectangle
{
anchors.verticalCenter: parent.verticalCenter
anchors.right: typesLegendModelLabel.right
width: UM.Theme.getSize("layerview_legend_size").width
height: UM.Theme.getSize("layerview_legend_size").height
color: UM.Theme.getColor(model.colorId)
border.width: UM.Theme.getSize("default_lining").width
border.color: UM.Theme.getColor("lining")
visible: viewSettings.show_legend
}
Layout.fillWidth: true
Layout.preferredHeight: UM.Theme.getSize("layerview_row").height + UM.Theme.getSize("default_lining").height
Layout.preferredWidth: UM.Theme.getSize("layerview_row").width
color: UM.Theme.getColor("setting_control_text")
font: UM.Theme.getFont("default")
}
}
// Text for the minimum, maximum and units for the feedrates and layer thickness
Item
{
id: gradientLegend
visible: viewSettings.show_gradient
width: parent.width
height: UM.Theme.getSize("layerview_row").height
anchors
{
topMargin: UM.Theme.getSize("slider_layerview_margin").height
horizontalCenter: parent.horizontalCenter
}
Label
{
text: minText()
anchors.left: parent.left
color: UM.Theme.getColor("setting_control_text")
font: UM.Theme.getFont("default")
function minText()
{
if (UM.SimulationView.layerActivity && CuraApplication.platformActivity)
{
// Feedrate selected
if (UM.Preferences.getValue("layerview/layer_view_type") == 2)
{
return parseFloat(UM.SimulationView.getMinFeedrate()).toFixed(2)
}
// Layer thickness selected
if (UM.Preferences.getValue("layerview/layer_view_type") == 3)
{
return parseFloat(UM.SimulationView.getMinThickness()).toFixed(2)
}
}
return catalog.i18nc("@label","min")
}
}
Label
{
text: unitsText()
anchors.horizontalCenter: parent.horizontalCenter
color: UM.Theme.getColor("setting_control_text")
font: UM.Theme.getFont("default")
function unitsText()
{
if (UM.SimulationView.layerActivity && CuraApplication.platformActivity)
{
// Feedrate selected
if (UM.Preferences.getValue("layerview/layer_view_type") == 2)
{
return "mm/s"
}
// Layer thickness selected
if (UM.Preferences.getValue("layerview/layer_view_type") == 3)
{
return "mm"
}
}
return ""
}
}
Label
{
text: maxText()
anchors.right: parent.right
color: UM.Theme.getColor("setting_control_text")
font: UM.Theme.getFont("default")
function maxText()
{
if (UM.SimulationView.layerActivity && CuraApplication.platformActivity)
{
// Feedrate selected
if (UM.Preferences.getValue("layerview/layer_view_type") == 2)
{
return parseFloat(UM.SimulationView.getMaxFeedrate()).toFixed(2)
}
// Layer thickness selected
if (UM.Preferences.getValue("layerview/layer_view_type") == 3)
{
return parseFloat(UM.SimulationView.getMaxThickness()).toFixed(2)
}
}
return catalog.i18nc("@label","max")
}
}
}
// Gradient colors for feedrate
Rectangle
{ // In QML 5.9 can be changed by LinearGradient
// Invert values because then the bar is rotated 90 degrees
id: feedrateGradient
visible: viewSettings.show_feedrate_gradient
anchors.left: parent.right
height: parent.width
width: Math.round(UM.Theme.getSize("layerview_row").height * 1.5)
border.width: UM.Theme.getSize("default_lining").width
border.color: UM.Theme.getColor("lining")
transform: Rotation {origin.x: 0; origin.y: 0; angle: 90}
gradient: Gradient
{
GradientStop
{
position: 0.000
color: Qt.rgba(1, 0.5, 0, 1)
}
GradientStop
{
position: 0.625
color: Qt.rgba(0.375, 0.5, 0, 1)
}
GradientStop
{
position: 0.75
color: Qt.rgba(0.25, 1, 0, 1)
}
GradientStop
{
position: 1.0
color: Qt.rgba(0, 0, 1, 1)
}
}
}
// Gradient colors for layer thickness (similar to parula colormap)
Rectangle // In QML 5.9 can be changed by LinearGradient
{
// Invert values because then the bar is rotated 90 degrees
id: thicknessGradient
visible: viewSettings.show_thickness_gradient
anchors.left: parent.right
height: parent.width
width: Math.round(UM.Theme.getSize("layerview_row").height * 1.5)
border.width: UM.Theme.getSize("default_lining").width
border.color: UM.Theme.getColor("lining")
transform: Rotation {origin.x: 0; origin.y: 0; angle: 90}
gradient: Gradient
{
GradientStop
{
position: 0.000
color: Qt.rgba(1, 1, 0, 1)
}
GradientStop
{
position: 0.25
color: Qt.rgba(1, 0.75, 0.25, 1)
}
GradientStop
{
position: 0.5
color: Qt.rgba(0, 0.75, 0.5, 1)
}
GradientStop
{
position: 0.75
color: Qt.rgba(0, 0.375, 0.75, 1)
}
GradientStop
{
position: 1.0
color: Qt.rgba(0, 0, 0.5, 1)
}
}
}
}
}
Item
{
id: slidersBox
width: parent.width
visible: UM.SimulationView.layerActivity && CuraApplication.platformActivity
anchors
{
top: parent.bottom
topMargin: UM.Theme.getSize("slider_layerview_margin").height
left: parent.left
}
PathSlider
{
id: pathSlider
height: UM.Theme.getSize("slider_handle").width
anchors.left: playButton.right
anchors.leftMargin: UM.Theme.getSize("default_margin").width
anchors.right: parent.right
visible: !UM.SimulationView.compatibilityMode
// custom properties
handleValue: UM.SimulationView.currentPath
maximumValue: UM.SimulationView.numPaths
handleSize: UM.Theme.getSize("slider_handle").width
trackThickness: UM.Theme.getSize("slider_groove").width
trackColor: UM.Theme.getColor("slider_groove")
trackBorderColor: UM.Theme.getColor("slider_groove_border")
handleColor: UM.Theme.getColor("slider_handle")
handleActiveColor: UM.Theme.getColor("slider_handle_active")
rangeColor: UM.Theme.getColor("slider_groove_fill")
// update values when layer data changes
Connections
{
target: UM.SimulationView
onMaxPathsChanged: pathSlider.setHandleValue(UM.SimulationView.currentPath)
onCurrentPathChanged:
{
// Only pause the simulation when the layer was changed manually, not when the simulation is running
if (pathSlider.manuallyChanged)
{
playButton.pauseSimulation()
}
pathSlider.setHandleValue(UM.SimulationView.currentPath)
}
}
// make sure the slider handlers show the correct value after switching views
Component.onCompleted:
{
pathSlider.setHandleValue(UM.SimulationView.currentPath)
}
}
LayerSlider
{
id: layerSlider
width: UM.Theme.getSize("slider_handle").width
height: UM.Theme.getSize("layerview_menu_size").height
anchors
{
top: !UM.SimulationView.compatibilityMode ? pathSlider.bottom : parent.top
topMargin: !UM.SimulationView.compatibilityMode ? UM.Theme.getSize("default_margin").height : 0
right: parent.right
rightMargin: UM.Theme.getSize("slider_layerview_margin").width
}
// custom properties
upperValue: UM.SimulationView.currentLayer
lowerValue: UM.SimulationView.minimumLayer
maximumValue: UM.SimulationView.numLayers
handleSize: UM.Theme.getSize("slider_handle").width
trackThickness: UM.Theme.getSize("slider_groove").width
trackColor: UM.Theme.getColor("slider_groove")
trackBorderColor: UM.Theme.getColor("slider_groove_border")
upperHandleColor: UM.Theme.getColor("slider_handle")
lowerHandleColor: UM.Theme.getColor("slider_handle")
rangeHandleColor: UM.Theme.getColor("slider_groove_fill")
handleActiveColor: UM.Theme.getColor("slider_handle_active")
handleLabelWidth: UM.Theme.getSize("slider_layerview_background").width
// update values when layer data changes
Connections
{
target: UM.SimulationView
onMaxLayersChanged: layerSlider.setUpperValue(UM.SimulationView.currentLayer)
onMinimumLayerChanged: layerSlider.setLowerValue(UM.SimulationView.minimumLayer)
onCurrentLayerChanged:
{
// Only pause the simulation when the layer was changed manually, not when the simulation is running
if (layerSlider.manuallyChanged)
{
playButton.pauseSimulation()
}
layerSlider.setUpperValue(UM.SimulationView.currentLayer)
}
}
// make sure the slider handlers show the correct value after switching views
Component.onCompleted:
{
layerSlider.setLowerValue(UM.SimulationView.minimumLayer)
layerSlider.setUpperValue(UM.SimulationView.currentLayer)
}
}
// Play simulation button
Button
{
id: playButton
iconSource: "./resources/simulation_resume.svg"
style: UM.Theme.styles.small_tool_button
visible: !UM.SimulationView.compatibilityMode
anchors
{
verticalCenter: pathSlider.verticalCenter
}
property var status: 0 // indicates if it's stopped (0) or playing (1)
onClicked:
{
switch(status)
{
case 0:
{
resumeSimulation()
break
}
case 1:
{
pauseSimulation()
break
}
}
}
function pauseSimulation()
{
UM.SimulationView.setSimulationRunning(false)
iconSource = "./resources/simulation_resume.svg"
simulationTimer.stop()
status = 0
layerSlider.manuallyChanged = true
pathSlider.manuallyChanged = true
}
function resumeSimulation()
{
UM.SimulationView.setSimulationRunning(true)
iconSource = "./resources/simulation_pause.svg"
simulationTimer.start()
layerSlider.manuallyChanged = false
pathSlider.manuallyChanged = false
}
}
Timer
{
id: simulationTimer
interval: 100
running: false
repeat: true
onTriggered:
{
var currentPath = UM.SimulationView.currentPath
var numPaths = UM.SimulationView.numPaths
var currentLayer = UM.SimulationView.currentLayer
var numLayers = UM.SimulationView.numLayers
// When the user plays the simulation, if the path slider is at the end of this layer, we start
// the simulation at the beginning of the current layer.
if (playButton.status == 0)
{
if (currentPath >= numPaths)
{
UM.SimulationView.setCurrentPath(0)
}
else
{
UM.SimulationView.setCurrentPath(currentPath+1)
}
}
// If the simulation is already playing and we reach the end of a layer, then it automatically
// starts at the beginning of the next layer.
else
{
if (currentPath >= numPaths)
{
// At the end of the model, the simulation stops
if (currentLayer >= numLayers)
{
playButton.pauseSimulation()
}
else
{
UM.SimulationView.setCurrentLayer(currentLayer+1)
UM.SimulationView.setCurrentPath(0)
}
}
else
{
UM.SimulationView.setCurrentPath(currentPath+1)
}
}
// The status must be set here instead of in the resumeSimulation function otherwise it won't work
// correctly, because part of the logic is in this trigger function.
playButton.status = 1
}
}
}
FontMetrics
{
id: fontMetrics
font: UM.Theme.getFont("default")
}
}

View file

@ -0,0 +1,211 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.4
import QtQuick.Controls 1.2
import QtQuick.Layouts 1.1
import QtQuick.Controls.Styles 1.1
import UM 1.4 as UM
import Cura 1.0 as Cura
Item
{
property bool is_simulation_playing: false
visible: UM.SimulationView.layerActivity && CuraApplication.platformActivity
PathSlider
{
id: pathSlider
height: UM.Theme.getSize("slider_handle").width
width: UM.Theme.getSize("slider_layerview_size").height
anchors.bottom: parent.bottom
anchors.bottomMargin: UM.Theme.getSize("default_margin").height
anchors.horizontalCenter: parent.horizontalCenter
visible: !UM.SimulationView.compatibilityMode
// Custom properties
handleValue: UM.SimulationView.currentPath
maximumValue: UM.SimulationView.numPaths
// Update values when layer data changes.
Connections
{
target: UM.SimulationView
onMaxPathsChanged: pathSlider.setHandleValue(UM.SimulationView.currentPath)
onCurrentPathChanged:
{
// Only pause the simulation when the layer was changed manually, not when the simulation is running
if (pathSlider.manuallyChanged)
{
playButton.pauseSimulation()
}
pathSlider.setHandleValue(UM.SimulationView.currentPath)
}
}
// Ensure that the slider handlers show the correct value after switching views.
Component.onCompleted:
{
pathSlider.setHandleValue(UM.SimulationView.currentPath)
}
}
UM.SimpleButton
{
id: playButton
iconSource: !is_simulation_playing ? "./resources/simulation_resume.svg": "./resources/simulation_pause.svg"
width: UM.Theme.getSize("small_button").width
height: UM.Theme.getSize("small_button").height
hoverColor: UM.Theme.getColor("slider_handle_active")
color: UM.Theme.getColor("slider_handle")
iconMargin: UM.Theme.getSize("thick_lining").width
visible: !UM.SimulationView.compatibilityMode
Connections
{
target: UM.Preferences
onPreferenceChanged:
{
playButton.pauseSimulation()
}
}
anchors
{
right: pathSlider.left
verticalCenter: pathSlider.verticalCenter
}
onClicked:
{
if(is_simulation_playing)
{
pauseSimulation()
}
else
{
resumeSimulation()
}
}
function pauseSimulation()
{
UM.SimulationView.setSimulationRunning(false)
simulationTimer.stop()
is_simulation_playing = false
layerSlider.manuallyChanged = true
pathSlider.manuallyChanged = true
}
function resumeSimulation()
{
UM.SimulationView.setSimulationRunning(true)
simulationTimer.start()
layerSlider.manuallyChanged = false
pathSlider.manuallyChanged = false
}
}
Timer
{
id: simulationTimer
interval: 100
running: false
repeat: true
onTriggered:
{
var currentPath = UM.SimulationView.currentPath
var numPaths = UM.SimulationView.numPaths
var currentLayer = UM.SimulationView.currentLayer
var numLayers = UM.SimulationView.numLayers
// When the user plays the simulation, if the path slider is at the end of this layer, we start
// the simulation at the beginning of the current layer.
if (!is_simulation_playing)
{
if (currentPath >= numPaths)
{
UM.SimulationView.setCurrentPath(0)
}
else
{
UM.SimulationView.setCurrentPath(currentPath + 1)
}
}
// If the simulation is already playing and we reach the end of a layer, then it automatically
// starts at the beginning of the next layer.
else
{
if (currentPath >= numPaths)
{
// At the end of the model, the simulation stops
if (currentLayer >= numLayers)
{
playButton.pauseSimulation()
}
else
{
UM.SimulationView.setCurrentLayer(currentLayer + 1)
UM.SimulationView.setCurrentPath(0)
}
}
else
{
UM.SimulationView.setCurrentPath(currentPath + 1)
}
}
// The status must be set here instead of in the resumeSimulation function otherwise it won't work
// correctly, because part of the logic is in this trigger function.
is_simulation_playing = true
}
}
LayerSlider
{
id: layerSlider
width: UM.Theme.getSize("slider_handle").width
height: UM.Theme.getSize("slider_layerview_size").height
anchors
{
right: parent.right
verticalCenter: parent.verticalCenter
rightMargin: UM.Theme.getSize("default_margin").width
}
// Custom properties
upperValue: UM.SimulationView.currentLayer
lowerValue: UM.SimulationView.minimumLayer
maximumValue: UM.SimulationView.numLayers
// Update values when layer data changes
Connections
{
target: UM.SimulationView
onMaxLayersChanged: layerSlider.setUpperValue(UM.SimulationView.currentLayer)
onMinimumLayerChanged: layerSlider.setLowerValue(UM.SimulationView.minimumLayer)
onCurrentLayerChanged:
{
// Only pause the simulation when the layer was changed manually, not when the simulation is running
if (layerSlider.manuallyChanged)
{
playButton.pauseSimulation()
}
layerSlider.setUpperValue(UM.SimulationView.currentLayer)
}
}
// Make sure the slider handlers show the correct value after switching views
Component.onCompleted:
{
layerSlider.setLowerValue(UM.SimulationView.minimumLayer)
layerSlider.setUpperValue(UM.SimulationView.currentLayer)
}
}
}

View file

@ -0,0 +1,530 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.4
import QtQuick.Controls 1.2
import QtQuick.Layouts 1.1
import QtQuick.Controls.Styles 1.1
import QtGraphicalEffects 1.0
import UM 1.0 as UM
import Cura 1.0 as Cura
Cura.ExpandableComponent
{
id: base
width: UM.Theme.getSize("layerview_menu_size").width
contentHeaderTitle: catalog.i18nc("@label", "Color scheme")
Connections
{
target: UM.Preferences
onPreferenceChanged:
{
layerTypeCombobox.currentIndex = UM.SimulationView.compatibilityMode ? 1 : UM.Preferences.getValue("layerview/layer_view_type")
layerTypeCombobox.updateLegends(layerTypeCombobox.currentIndex)
viewSettings.extruder_opacities = UM.Preferences.getValue("layerview/extruder_opacities").split("|")
viewSettings.show_travel_moves = UM.Preferences.getValue("layerview/show_travel_moves")
viewSettings.show_helpers = UM.Preferences.getValue("layerview/show_helpers")
viewSettings.show_skin = UM.Preferences.getValue("layerview/show_skin")
viewSettings.show_infill = UM.Preferences.getValue("layerview/show_infill")
viewSettings.only_show_top_layers = UM.Preferences.getValue("view/only_show_top_layers")
viewSettings.top_layer_count = UM.Preferences.getValue("view/top_layer_count")
}
}
headerItem: Label
{
id: layerViewTypesLabel
text: catalog.i18nc("@label", "Color scheme")
font: UM.Theme.getFont("default")
color: UM.Theme.getColor("setting_control_text")
height: base.height
verticalAlignment: Text.AlignVCenter
}
contentItem: Column
{
id: viewSettings
property var extruder_opacities: UM.Preferences.getValue("layerview/extruder_opacities").split("|")
property bool show_travel_moves: UM.Preferences.getValue("layerview/show_travel_moves")
property bool show_helpers: UM.Preferences.getValue("layerview/show_helpers")
property bool show_skin: UM.Preferences.getValue("layerview/show_skin")
property bool show_infill: UM.Preferences.getValue("layerview/show_infill")
// If we are in compatibility mode, we only show the "line type"
property bool show_legend: UM.SimulationView.compatibilityMode ? true : UM.Preferences.getValue("layerview/layer_view_type") == 1
property bool show_gradient: UM.SimulationView.compatibilityMode ? false : UM.Preferences.getValue("layerview/layer_view_type") == 2 || UM.Preferences.getValue("layerview/layer_view_type") == 3
property bool show_feedrate_gradient: show_gradient && UM.Preferences.getValue("layerview/layer_view_type") == 2
property bool show_thickness_gradient: show_gradient && UM.Preferences.getValue("layerview/layer_view_type") == 3
property bool only_show_top_layers: UM.Preferences.getValue("view/only_show_top_layers")
property int top_layer_count: UM.Preferences.getValue("view/top_layer_count")
width: UM.Theme.getSize("layerview_menu_size").width - 2 * UM.Theme.getSize("default_margin").width
height: implicitHeight
spacing: UM.Theme.getSize("layerview_row_spacing").height
ListModel // matches SimulationView.py
{
id: layerViewTypes
}
Component.onCompleted:
{
layerViewTypes.append({
text: catalog.i18nc("@label:listbox", "Material Color"),
type_id: 0
})
layerViewTypes.append({
text: catalog.i18nc("@label:listbox", "Line Type"),
type_id: 1
})
layerViewTypes.append({
text: catalog.i18nc("@label:listbox", "Feedrate"),
type_id: 2
})
layerViewTypes.append({
text: catalog.i18nc("@label:listbox", "Layer thickness"),
type_id: 3 // these ids match the switching in the shader
})
}
ComboBox
{
id: layerTypeCombobox
width: parent.width
model: layerViewTypes
visible: !UM.SimulationView.compatibilityMode
style: UM.Theme.styles.combobox
onActivated:
{
UM.Preferences.setValue("layerview/layer_view_type", index);
}
Component.onCompleted:
{
currentIndex = UM.SimulationView.compatibilityMode ? 1 : UM.Preferences.getValue("layerview/layer_view_type");
updateLegends(currentIndex);
}
function updateLegends(type_id)
{
// Update the visibility of the legends.
viewSettings.show_legend = UM.SimulationView.compatibilityMode || (type_id == 1);
viewSettings.show_gradient = !UM.SimulationView.compatibilityMode && (type_id == 2 || type_id == 3);
viewSettings.show_feedrate_gradient = viewSettings.show_gradient && (type_id == 2);
viewSettings.show_thickness_gradient = viewSettings.show_gradient && (type_id == 3);
}
}
Label
{
id: compatibilityModeLabel
text: catalog.i18nc("@label","Compatibility Mode")
font: UM.Theme.getFont("default")
color: UM.Theme.getColor("text")
visible: UM.SimulationView.compatibilityMode
height: UM.Theme.getSize("layerview_row").height
width: parent.width
renderType: Text.NativeRendering
}
Item // Spacer
{
height: Math.round(UM.Theme.getSize("default_margin").width / 2)
width: width
}
Repeater
{
model: Cura.ExtrudersModel{}
CheckBox
{
id: extrudersModelCheckBox
checked: viewSettings.extruder_opacities[index] > 0.5 || viewSettings.extruder_opacities[index] == undefined || viewSettings.extruder_opacities[index] == ""
height: UM.Theme.getSize("layerview_row").height + UM.Theme.getSize("default_lining").height
width: parent.width
visible: !UM.SimulationView.compatibilityMode
enabled: index < 4
onClicked:
{
viewSettings.extruder_opacities[index] = checked ? 1.0 : 0.0
UM.Preferences.setValue("layerview/extruder_opacities", viewSettings.extruder_opacities.join("|"));
}
style: UM.Theme.styles.checkbox
Rectangle
{
anchors.verticalCenter: parent.verticalCenter
anchors.right: extrudersModelCheckBox.right
width: UM.Theme.getSize("layerview_legend_size").width
height: UM.Theme.getSize("layerview_legend_size").height
color: model.color
radius: Math.round(width / 2)
border.width: UM.Theme.getSize("default_lining").width
border.color: UM.Theme.getColor("lining")
visible: !viewSettings.show_legend && !viewSettings.show_gradient
}
Label
{
text: model.name
elide: Text.ElideRight
color: UM.Theme.getColor("setting_control_text")
font: UM.Theme.getFont("default")
anchors
{
verticalCenter: parent.verticalCenter
left: extrudersModelCheckBox.left
right: extrudersModelCheckBox.right
leftMargin: UM.Theme.getSize("checkbox").width + Math.round(UM.Theme.getSize("default_margin").width / 2)
rightMargin: UM.Theme.getSize("default_margin").width * 2
}
renderType: Text.NativeRendering
}
}
}
Repeater
{
model: ListModel
{
id: typesLegendModel
Component.onCompleted:
{
typesLegendModel.append({
label: catalog.i18nc("@label", "Show Travels"),
initialValue: viewSettings.show_travel_moves,
preference: "layerview/show_travel_moves",
colorId: "layerview_move_combing"
});
typesLegendModel.append({
label: catalog.i18nc("@label", "Show Helpers"),
initialValue: viewSettings.show_helpers,
preference: "layerview/show_helpers",
colorId: "layerview_support"
});
typesLegendModel.append({
label: catalog.i18nc("@label", "Show Shell"),
initialValue: viewSettings.show_skin,
preference: "layerview/show_skin",
colorId: "layerview_inset_0"
});
typesLegendModel.append({
label: catalog.i18nc("@label", "Show Infill"),
initialValue: viewSettings.show_infill,
preference: "layerview/show_infill",
colorId: "layerview_infill"
});
}
}
CheckBox
{
id: legendModelCheckBox
checked: model.initialValue
onClicked: UM.Preferences.setValue(model.preference, checked)
height: UM.Theme.getSize("layerview_row").height + UM.Theme.getSize("default_lining").height
width: parent.width
style: UM.Theme.styles.checkbox
Rectangle
{
anchors.verticalCenter: parent.verticalCenter
anchors.right: legendModelCheckBox.right
width: UM.Theme.getSize("layerview_legend_size").width
height: UM.Theme.getSize("layerview_legend_size").height
color: UM.Theme.getColor(model.colorId)
border.width: UM.Theme.getSize("default_lining").width
border.color: UM.Theme.getColor("lining")
visible: viewSettings.show_legend
}
Label
{
text: label
font: UM.Theme.getFont("default")
elide: Text.ElideRight
renderType: Text.NativeRendering
color: UM.Theme.getColor("setting_control_text")
anchors.verticalCenter: parent.verticalCenter
anchors.left: legendModelCheckBox.left
anchors.right: legendModelCheckBox.right
anchors.leftMargin: UM.Theme.getSize("checkbox").width + Math.round(UM.Theme.getSize("default_margin").width / 2)
anchors.rightMargin: UM.Theme.getSize("default_margin").width * 2
}
}
}
CheckBox
{
checked: viewSettings.only_show_top_layers
onClicked: UM.Preferences.setValue("view/only_show_top_layers", checked ? 1.0 : 0.0)
text: catalog.i18nc("@label", "Only Show Top Layers")
visible: UM.SimulationView.compatibilityMode
style: UM.Theme.styles.checkbox
width: parent.width
}
CheckBox
{
checked: viewSettings.top_layer_count == 5
onClicked: UM.Preferences.setValue("view/top_layer_count", checked ? 5 : 1)
text: catalog.i18nc("@label", "Show 5 Detailed Layers On Top")
width: parent.width
visible: UM.SimulationView.compatibilityMode
style: UM.Theme.styles.checkbox
}
Repeater
{
model: ListModel
{
id: typesLegendModelNoCheck
Component.onCompleted:
{
typesLegendModelNoCheck.append({
label: catalog.i18nc("@label", "Top / Bottom"),
colorId: "layerview_skin",
});
typesLegendModelNoCheck.append({
label: catalog.i18nc("@label", "Inner Wall"),
colorId: "layerview_inset_x",
});
}
}
Label
{
text: label
visible: viewSettings.show_legend
id: typesLegendModelLabel
height: UM.Theme.getSize("layerview_row").height + UM.Theme.getSize("default_lining").height
width: parent.width
color: UM.Theme.getColor("setting_control_text")
font: UM.Theme.getFont("default")
renderType: Text.NativeRendering
Rectangle
{
anchors.verticalCenter: parent.verticalCenter
anchors.right: typesLegendModelLabel.right
width: UM.Theme.getSize("layerview_legend_size").width
height: UM.Theme.getSize("layerview_legend_size").height
color: UM.Theme.getColor(model.colorId)
border.width: UM.Theme.getSize("default_lining").width
border.color: UM.Theme.getColor("lining")
}
}
}
// Text for the minimum, maximum and units for the feedrates and layer thickness
Item
{
id: gradientLegend
visible: viewSettings.show_gradient
width: parent.width
height: UM.Theme.getSize("layerview_row").height
Label
{
text:
{
if (UM.SimulationView.layerActivity && CuraApplication.platformActivity)
{
// Feedrate selected
if (UM.Preferences.getValue("layerview/layer_view_type") == 2)
{
return parseFloat(UM.SimulationView.getMinFeedrate()).toFixed(2)
}
// Layer thickness selected
if (UM.Preferences.getValue("layerview/layer_view_type") == 3)
{
return parseFloat(UM.SimulationView.getMinThickness()).toFixed(2)
}
}
return catalog.i18nc("@label","min")
}
anchors.left: parent.left
color: UM.Theme.getColor("setting_control_text")
font: UM.Theme.getFont("default")
renderType: Text.NativeRendering
}
Label
{
text: {
if (UM.SimulationView.layerActivity && CuraApplication.platformActivity)
{
// Feedrate selected
if (UM.Preferences.getValue("layerview/layer_view_type") == 2)
{
return "mm/s"
}
// Layer thickness selected
if (UM.Preferences.getValue("layerview/layer_view_type") == 3)
{
return "mm"
}
}
return ""
}
anchors.horizontalCenter: parent.horizontalCenter
color: UM.Theme.getColor("setting_control_text")
font: UM.Theme.getFont("default")
}
Label
{
text: {
if (UM.SimulationView.layerActivity && CuraApplication.platformActivity)
{
// Feedrate selected
if (UM.Preferences.getValue("layerview/layer_view_type") == 2)
{
return parseFloat(UM.SimulationView.getMaxFeedrate()).toFixed(2)
}
// Layer thickness selected
if (UM.Preferences.getValue("layerview/layer_view_type") == 3)
{
return parseFloat(UM.SimulationView.getMaxThickness()).toFixed(2)
}
}
return catalog.i18nc("@label","max")
}
anchors.right: parent.right
color: UM.Theme.getColor("setting_control_text")
font: UM.Theme.getFont("default")
}
}
// Gradient colors for feedrate
Rectangle
{
id: feedrateGradient
visible: viewSettings.show_feedrate_gradient
anchors.left: parent.left
anchors.right: parent.right
height: Math.round(UM.Theme.getSize("layerview_row").height * 1.5)
border.width: UM.Theme.getSize("default_lining").width
border.color: UM.Theme.getColor("lining")
LinearGradient
{
anchors
{
left: parent.left
leftMargin: UM.Theme.getSize("default_lining").width
right: parent.right
rightMargin: UM.Theme.getSize("default_lining").width
top: parent.top
topMargin: UM.Theme.getSize("default_lining").width
bottom: parent.bottom
bottomMargin: UM.Theme.getSize("default_lining").width
}
start: Qt.point(0, 0)
end: Qt.point(parent.width, 0)
gradient: Gradient
{
GradientStop
{
position: 0.000
color: Qt.rgba(0, 0, 1, 1)
}
GradientStop
{
position: 0.25
color: Qt.rgba(0.25, 1, 0, 1)
}
GradientStop
{
position: 0.375
color: Qt.rgba(0.375, 0.5, 0, 1)
}
GradientStop
{
position: 1.0
color: Qt.rgba(1, 0.5, 0, 1)
}
}
}
}
// Gradient colors for layer thickness (similar to parula colormap)
Rectangle
{
id: thicknessGradient
visible: viewSettings.show_thickness_gradient
anchors.left: parent.left
anchors.right: parent.right
height: Math.round(UM.Theme.getSize("layerview_row").height * 1.5)
border.width: UM.Theme.getSize("default_lining").width
border.color: UM.Theme.getColor("lining")
LinearGradient
{
anchors
{
left: parent.left
leftMargin: UM.Theme.getSize("default_lining").width
right: parent.right
rightMargin: UM.Theme.getSize("default_lining").width
top: parent.top
topMargin: UM.Theme.getSize("default_lining").width
bottom: parent.bottom
bottomMargin: UM.Theme.getSize("default_lining").width
}
start: Qt.point(0, 0)
end: Qt.point(parent.width, 0)
gradient: Gradient
{
GradientStop
{
position: 0.000
color: Qt.rgba(0, 0, 0.5, 1)
}
GradientStop
{
position: 0.25
color: Qt.rgba(0, 0.375, 0.75, 1)
}
GradientStop
{
position: 0.5
color: Qt.rgba(0, 0.75, 0.5, 1)
}
GradientStop
{
position: 0.75
color: Qt.rgba(1, 0.75, 0.25, 1)
}
GradientStop
{
position: 1.0
color: Qt.rgba(1, 1, 0, 1)
}
}
}
}
}
FontMetrics
{
id: fontMetrics
font: UM.Theme.getFont("default")
}
}

View file

@ -8,19 +8,21 @@ from . import SimulationViewProxy, SimulationView
catalog = i18nCatalog("cura") catalog = i18nCatalog("cura")
def getMetaData(): def getMetaData():
return { return {
"view": { "view": {
"name": catalog.i18nc("@item:inlistbox", "Layer view"), "name": catalog.i18nc("@item:inlistbox", "Layer view"),
"view_panel": "SimulationView.qml", "weight": 0
"weight": 2
} }
} }
def createSimulationViewProxy(engine, script_engine): def createSimulationViewProxy(engine, script_engine):
return SimulationViewProxy.SimulationViewProxy() return SimulationViewProxy.SimulationViewProxy()
def register(app): def register(app):
simulation_view = SimulationView.SimulationView() simulation_view = SimulationView.SimulationView()
qmlRegisterSingletonType(SimulationViewProxy.SimulationViewProxy, "UM", 1, 0, "SimulationView", simulation_view.getProxy) qmlRegisterSingletonType(SimulationViewProxy.SimulationViewProxy, "UM", 1, 0, "SimulationView", simulation_view.getProxy)
return { "view": SimulationView.SimulationView()} return { "view": simulation_view}

View file

@ -10,7 +10,8 @@ def getMetaData():
return { return {
"view": { "view": {
"name": i18n_catalog.i18nc("@item:inmenu", "Solid view"), "name": i18n_catalog.i18nc("@item:inmenu", "Solid view"),
"weight": 0 "weight": 0,
"visible": False
} }
} }

View file

@ -20,11 +20,11 @@ Window
maximumWidth: minimumWidth maximumWidth: minimumWidth
minimumHeight: height minimumHeight: height
maximumHeight: minimumHeight maximumHeight: minimumHeight
color: UM.Theme.getColor("sidebar") color: UM.Theme.getColor("main_background")
UM.I18nCatalog UM.I18nCatalog
{ {
id: catalog id: catalog
name:"cura" name: "cura"
} }
Item Item
{ {

View file

@ -86,13 +86,13 @@ Item
Label Label
{ {
text: catalog.i18nc("@label", "Website") + ":" text: catalog.i18nc("@label", "Website") + ":"
font: UM.Theme.getFont("very_small") font: UM.Theme.getFont("default")
color: UM.Theme.getColor("text_medium") color: UM.Theme.getColor("text_medium")
} }
Label Label
{ {
text: catalog.i18nc("@label", "Email") + ":" text: catalog.i18nc("@label", "Email") + ":"
font: UM.Theme.getFont("very_small") font: UM.Theme.getFont("default")
color: UM.Theme.getColor("text_medium") color: UM.Theme.getColor("text_medium")
} }
} }
@ -118,7 +118,7 @@ Item
} }
return "" return ""
} }
font: UM.Theme.getFont("very_small") font: UM.Theme.getFont("default")
color: UM.Theme.getColor("text") color: UM.Theme.getColor("text")
linkColor: UM.Theme.getColor("text_link") linkColor: UM.Theme.getColor("text_link")
onLinkActivated: Qt.openUrlExternally(link) onLinkActivated: Qt.openUrlExternally(link)
@ -134,7 +134,7 @@ Item
} }
return "" return ""
} }
font: UM.Theme.getFont("very_small") font: UM.Theme.getFont("default")
color: UM.Theme.getColor("text") color: UM.Theme.getColor("text")
linkColor: UM.Theme.getColor("text_link") linkColor: UM.Theme.getColor("text_link")
onLinkActivated: Qt.openUrlExternally(link) onLinkActivated: Qt.openUrlExternally(link)

View file

@ -90,7 +90,7 @@ Item
model: packageData.supported_configs model: packageData.supported_configs
headerDelegate: Rectangle headerDelegate: Rectangle
{ {
color: UM.Theme.getColor("sidebar") color: UM.Theme.getColor("main_background")
height: UM.Theme.getSize("toolbox_chart_row").height height: UM.Theme.getSize("toolbox_chart_row").height
Label Label
{ {
@ -228,7 +228,7 @@ Item
return result return result
} }
font: UM.Theme.getFont("very_small") font: UM.Theme.getFont("default")
color: UM.Theme.getColor("text") color: UM.Theme.getColor("text")
linkColor: UM.Theme.getColor("text_link") linkColor: UM.Theme.getColor("text_link")
onLinkActivated: Qt.openUrlExternally(link) onLinkActivated: Qt.openUrlExternally(link)

View file

@ -37,7 +37,7 @@ Item
leftMargin: UM.Theme.getSize("wide_margin").width leftMargin: UM.Theme.getSize("wide_margin").width
topMargin: UM.Theme.getSize("wide_margin").height topMargin: UM.Theme.getSize("wide_margin").height
} }
color: white //Always a white background for image (regardless of theme). color: "white" //Always a white background for image (regardless of theme).
Image Image
{ {
anchors.fill: parent anchors.fill: parent
@ -82,25 +82,25 @@ Item
Label Label
{ {
text: catalog.i18nc("@label", "Version") + ":" text: catalog.i18nc("@label", "Version") + ":"
font: UM.Theme.getFont("very_small") font: UM.Theme.getFont("default")
color: UM.Theme.getColor("text_medium") color: UM.Theme.getColor("text_medium")
} }
Label Label
{ {
text: catalog.i18nc("@label", "Last updated") + ":" text: catalog.i18nc("@label", "Last updated") + ":"
font: UM.Theme.getFont("very_small") font: UM.Theme.getFont("default")
color: UM.Theme.getColor("text_medium") color: UM.Theme.getColor("text_medium")
} }
Label Label
{ {
text: catalog.i18nc("@label", "Author") + ":" text: catalog.i18nc("@label", "Author") + ":"
font: UM.Theme.getFont("very_small") font: UM.Theme.getFont("default")
color: UM.Theme.getColor("text_medium") color: UM.Theme.getColor("text_medium")
} }
Label Label
{ {
text: catalog.i18nc("@label", "Downloads") + ":" text: catalog.i18nc("@label", "Downloads") + ":"
font: UM.Theme.getFont("very_small") font: UM.Theme.getFont("default")
color: UM.Theme.getColor("text_medium") color: UM.Theme.getColor("text_medium")
} }
} }
@ -119,7 +119,7 @@ Item
Label Label
{ {
text: details === null ? "" : (details.version || catalog.i18nc("@label", "Unknown")) text: details === null ? "" : (details.version || catalog.i18nc("@label", "Unknown"))
font: UM.Theme.getFont("very_small") font: UM.Theme.getFont("default")
color: UM.Theme.getColor("text") color: UM.Theme.getColor("text")
} }
Label Label
@ -133,7 +133,7 @@ Item
var date = new Date(details.last_updated) var date = new Date(details.last_updated)
return date.toLocaleString(UM.Preferences.getValue("general/language")) return date.toLocaleString(UM.Preferences.getValue("general/language"))
} }
font: UM.Theme.getFont("very_small") font: UM.Theme.getFont("default")
color: UM.Theme.getColor("text") color: UM.Theme.getColor("text")
} }
Label Label
@ -149,7 +149,7 @@ Item
return "<a href=\"" + details.website + "\">" + details.author_name + "</a>" return "<a href=\"" + details.website + "\">" + details.author_name + "</a>"
} }
} }
font: UM.Theme.getFont("very_small") font: UM.Theme.getFont("default")
color: UM.Theme.getColor("text") color: UM.Theme.getColor("text")
linkColor: UM.Theme.getColor("text_link") linkColor: UM.Theme.getColor("text_link")
onLinkActivated: Qt.openUrlExternally(link) onLinkActivated: Qt.openUrlExternally(link)
@ -157,7 +157,7 @@ Item
Label Label
{ {
text: details === null ? "" : (details.download_count || catalog.i18nc("@label", "Unknown")) text: details === null ? "" : (details.download_count || catalog.i18nc("@label", "Unknown"))
font: UM.Theme.getFont("very_small") font: UM.Theme.getFont("default")
color: UM.Theme.getColor("text") color: UM.Theme.getColor("text")
} }
} }

View file

@ -52,7 +52,6 @@ Item
bottom: parent.bottom bottom: parent.bottom
right: parent.right right: parent.right
} }
sourceSize.width: width
sourceSize.height: height sourceSize.height: height
visible: installedPackages != 0 visible: installedPackages != 0
color: (installedPackages == packageCount) ? UM.Theme.getColor("primary") : UM.Theme.getColor("border") color: (installedPackages == packageCount) ? UM.Theme.getColor("primary") : UM.Theme.getColor("border")
@ -81,7 +80,7 @@ Item
width: parent.width width: parent.width
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
color: UM.Theme.getColor("text_medium") color: UM.Theme.getColor("text_medium")
font: UM.Theme.getFont("very_small") font: UM.Theme.getFont("default")
} }
} }
} }

View file

@ -48,8 +48,6 @@ Rectangle
right: parent.right right: parent.right
bottomMargin: UM.Theme.getSize("default_lining").width bottomMargin: UM.Theme.getSize("default_lining").width
} }
sourceSize.width: width
sourceSize.height: height
visible: installedPackages != 0 visible: installedPackages != 0
color: (installedPackages == packageCount) ? UM.Theme.getColor("primary") : UM.Theme.getColor("border") color: (installedPackages == packageCount) ? UM.Theme.getColor("primary") : UM.Theme.getColor("border")
source: "../images/installed_check.svg" source: "../images/installed_check.svg"

View file

@ -19,10 +19,10 @@ Button
Rectangle Rectangle
{ {
visible: control.active visible: control.active
color: UM.Theme.getColor("sidebar_header_highlight_hover") color: UM.Theme.getColor("toolbox_header_highlight_hover")
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
width: parent.width width: parent.width
height: UM.Theme.getSize("sidebar_header_highlight").height height: UM.Theme.getSize("toolbox_header_highlight").height
} }
} }
label: Label label: Label
@ -32,15 +32,15 @@ Button
{ {
if(control.hovered) if(control.hovered)
{ {
return UM.Theme.getColor("topbar_button_text_hovered"); return UM.Theme.getColor("toolbox_header_button_text_hovered");
} }
if(control.active) if(control.active)
{ {
return UM.Theme.getColor("topbar_button_text_active"); return UM.Theme.getColor("toolbox_header_button_text_active");
} }
else else
{ {
return UM.Theme.getColor("topbar_button_text_inactive"); return UM.Theme.getColor("toolbox_header_button_text_inactive");
} }
} }
font: control.enabled ? (control.active ? UM.Theme.getFont("medium_bold") : UM.Theme.getFont("medium")) : UM.Theme.getFont("default_italic") font: control.enabled ? (control.active ? UM.Theme.getFont("medium_bold") : UM.Theme.getFont("medium")) : UM.Theme.getFont("default_italic")

View file

@ -1,110 +0,0 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.3
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.3
import UM 1.3 as UM
import Cura 1.0 as Cura
Component {
Rectangle {
id: base;
property var shadowRadius: UM.Theme.getSize("monitor_shadow_radius").width;
property var cornerRadius: UM.Theme.getSize("monitor_corner_radius").width;
anchors.fill: parent;
color: UM.Theme.getColor("sidebar");
visible: OutputDevice != null;
UM.I18nCatalog {
id: catalog;
name: "cura";
}
Label {
id: printingLabel;
anchors {
left: parent.left;
leftMargin: 4 * UM.Theme.getSize("default_margin").width;
margins: 2 * UM.Theme.getSize("default_margin").width;
right: parent.right;
top: parent.top;
}
color: UM.Theme.getColor("text");
elide: Text.ElideRight;
font: UM.Theme.getFont("large");
text: catalog.i18nc("@label", "Printing");
}
Label {
id: managePrintersLabel;
anchors {
bottom: printingLabel.bottom;
right: printerScrollView.right;
rightMargin: 4 * UM.Theme.getSize("default_margin").width;
}
color: UM.Theme.getColor("primary"); // "Cura Blue"
font: UM.Theme.getFont("default");
linkColor: UM.Theme.getColor("primary"); // "Cura Blue"
text: catalog.i18nc("@label link to connect manager", "Manage printers");
}
MouseArea {
anchors.fill: managePrintersLabel;
hoverEnabled: true;
onClicked: Cura.MachineManager.printerOutputDevices[0].openPrinterControlPanel();
onEntered: managePrintersLabel.font.underline = true;
onExited: managePrintersLabel.font.underline = false;
}
// Skeleton loading
Column {
id: skeletonLoader;
anchors {
left: parent.left;
leftMargin: UM.Theme.getSize("wide_margin").width;
right: parent.right;
rightMargin: UM.Theme.getSize("wide_margin").width;
top: printingLabel.bottom;
topMargin: UM.Theme.getSize("default_margin").height;
}
spacing: UM.Theme.getSize("default_margin").height - 10;
visible: printerList.count === 0;
PrinterCard {
printer: null;
}
PrinterCard {
printer: null;
}
}
// Actual content
ScrollView {
id: printerScrollView;
anchors {
bottom: parent.bottom;
left: parent.left;
right: parent.right;
top: printingLabel.bottom;
topMargin: UM.Theme.getSize("default_margin").height;
}
style: UM.Theme.styles.scrollview;
ListView {
id: printerList;
property var currentIndex: -1;
anchors {
fill: parent;
leftMargin: UM.Theme.getSize("wide_margin").width;
rightMargin: UM.Theme.getSize("wide_margin").width;
}
delegate: PrinterCard {
printer: modelData;
}
model: OutputDevice.printers;
spacing: UM.Theme.getSize("default_margin").height - 10;
}
}
}
}

View file

@ -64,6 +64,7 @@ Cura.MachineAction
width: parent.width width: parent.width
text: catalog.i18nc("@title:window", "Connect to Networked Printer") text: catalog.i18nc("@title:window", "Connect to Networked Printer")
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
renderType: Text.NativeRendering
font.pointSize: 18 font.pointSize: 18
} }
@ -72,6 +73,7 @@ Cura.MachineAction
id: pageDescription id: pageDescription
width: parent.width width: parent.width
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
renderType: Text.NativeRendering
text: catalog.i18nc("@label", "To print directly to your printer over the network, please make sure your printer is connected to the network using a network cable or by connecting your printer to your WIFI network. If you don't connect Cura with your printer, you can still use a USB drive to transfer g-code files to your printer.\n\nSelect your printer from the list below:") text: catalog.i18nc("@label", "To print directly to your printer over the network, please make sure your printer is connected to the network using a network cable or by connecting your printer to your WIFI network. If you don't connect Cura with your printer, you can still use a USB drive to transfer g-code files to your printer.\n\nSelect your printer from the list below:")
} }
@ -182,6 +184,7 @@ Cura.MachineAction
text: listview.model[index].name text: listview.model[index].name
color: parent.ListView.isCurrentItem ? palette.highlightedText : palette.text color: parent.ListView.isCurrentItem ? palette.highlightedText : palette.text
elide: Text.ElideRight elide: Text.ElideRight
renderType: Text.NativeRendering
} }
MouseArea MouseArea
@ -204,6 +207,7 @@ Cura.MachineAction
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
renderType: Text.NativeRendering
text: catalog.i18nc("@label", "If your printer is not listed, read the <a href='%1'>network printing troubleshooting guide</a>").arg("https://ultimaker.com/en/troubleshooting"); text: catalog.i18nc("@label", "If your printer is not listed, read the <a href='%1'>network printing troubleshooting guide</a>").arg("https://ultimaker.com/en/troubleshooting");
onLinkActivated: Qt.openUrlExternally(link) onLinkActivated: Qt.openUrlExternally(link)
} }
@ -221,6 +225,7 @@ Cura.MachineAction
text: base.selectedDevice ? base.selectedDevice.name : "" text: base.selectedDevice ? base.selectedDevice.name : ""
font: UM.Theme.getFont("large") font: UM.Theme.getFont("large")
elide: Text.ElideRight elide: Text.ElideRight
renderType: Text.NativeRendering
} }
Grid Grid
{ {
@ -231,12 +236,14 @@ Cura.MachineAction
{ {
width: Math.round(parent.width * 0.5) width: Math.round(parent.width * 0.5)
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
renderType: Text.NativeRendering
text: catalog.i18nc("@label", "Type") text: catalog.i18nc("@label", "Type")
} }
Label Label
{ {
width: Math.round(parent.width * 0.5) width: Math.round(parent.width * 0.5)
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
renderType: Text.NativeRendering
text: text:
{ {
if(base.selectedDevice) if(base.selectedDevice)
@ -268,24 +275,28 @@ Cura.MachineAction
{ {
width: Math.round(parent.width * 0.5) width: Math.round(parent.width * 0.5)
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
renderType: Text.NativeRendering
text: catalog.i18nc("@label", "Firmware version") text: catalog.i18nc("@label", "Firmware version")
} }
Label Label
{ {
width: Math.round(parent.width * 0.5) width: Math.round(parent.width * 0.5)
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
renderType: Text.NativeRendering
text: base.selectedDevice ? base.selectedDevice.firmwareVersion : "" text: base.selectedDevice ? base.selectedDevice.firmwareVersion : ""
} }
Label Label
{ {
width: Math.round(parent.width * 0.5) width: Math.round(parent.width * 0.5)
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
renderType: Text.NativeRendering
text: catalog.i18nc("@label", "Address") text: catalog.i18nc("@label", "Address")
} }
Label Label
{ {
width: Math.round(parent.width * 0.5) width: Math.round(parent.width * 0.5)
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
renderType: Text.NativeRendering
text: base.selectedDevice ? base.selectedDevice.ipAddress : "" text: base.selectedDevice ? base.selectedDevice.ipAddress : ""
} }
} }
@ -294,6 +305,7 @@ Cura.MachineAction
{ {
width: parent.width width: parent.width
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
renderType: Text.NativeRendering
text:{ text:{
// The property cluster size does not exist for older UM3 devices. // The property cluster size does not exist for older UM3 devices.
if(!base.selectedDevice || base.selectedDevice.clusterSize == null || base.selectedDevice.clusterSize == 1) if(!base.selectedDevice || base.selectedDevice.clusterSize == null || base.selectedDevice.clusterSize == 1)
@ -315,6 +327,7 @@ Cura.MachineAction
{ {
width: parent.width width: parent.width
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
renderType: Text.NativeRendering
visible: base.selectedDevice != null && !base.completeProperties visible: base.selectedDevice != null && !base.completeProperties
text: catalog.i18nc("@label", "The printer at this address has not yet responded." ) text: catalog.i18nc("@label", "The printer at this address has not yet responded." )
} }
@ -358,9 +371,10 @@ Cura.MachineAction
Label Label
{ {
text: catalog.i18nc("@alabel","Enter the IP address or hostname of your printer on the network.") text: catalog.i18nc("@alabel", "Enter the IP address or hostname of your printer on the network.")
width: parent.width width: parent.width
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
renderType: Text.NativeRendering
} }
TextField TextField

View file

@ -1,12 +0,0 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.3
import QtQuick.Controls 2.0
import UM 1.3 as UM
Rectangle {
color: UM.Theme.getColor("monitor_lining_light"); // TODO: Maybe theme separately? Maybe not.
height: UM.Theme.getSize("default_lining").height;
width: parent.width;
}

View file

@ -27,7 +27,7 @@ Item
Row Row
{ {
height: parent.height height: parent.height
spacing: 12 * screenScaleFactor // TODO: Theme! (Should be same as extruder spacing) spacing: UM.Theme.getSize("print_setup_slider_handle").width // TODO: Theme! (Should be same as extruder spacing)
// This wrapper ensures that the buildplate icon is located centered // This wrapper ensures that the buildplate icon is located centered
// below an extruder icon. // below an extruder icon.
@ -52,7 +52,7 @@ Item
id: buildplateLabel id: buildplateLabel
color: "#191919" // TODO: Theme! color: "#191919" // TODO: Theme!
elide: Text.ElideRight elide: Text.ElideRight
font: UM.Theme.getFont("very_small") // 12pt, regular font: UM.Theme.getFont("default") // 12pt, regular
text: "" text: ""
// FIXED-LINE-HEIGHT: // FIXED-LINE-HEIGHT:

View file

@ -0,0 +1,142 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.3
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.3
import QtQuick.Dialogs 1.2
import UM 1.3 as UM
UM.Dialog
{
id: overrideConfirmationDialog
property var printer: null
minimumWidth: screenScaleFactor * 640;
minimumHeight: screenScaleFactor * 320;
width: minimumWidth
height: minimumHeight
title: catalog.i18nc("@title:window", "Configuration Changes")
rightButtons:
[
Button
{
id: overrideButton
anchors.margins: UM.Theme.getSize("default_margin").width
text: catalog.i18nc("@action:button", "Override")
onClicked:
{
OutputDevice.forceSendJob(printer.activePrintJob.key)
overrideConfirmationDialog.close()
}
},
Button
{
id: cancelButton
anchors.margins: UM.Theme.getSize("default_margin").width
text: catalog.i18nc("@action:button", "Cancel")
onClicked:
{
overrideConfirmationDialog.reject()
}
}
]
Label
{
anchors
{
fill: parent
margins: 36 * screenScaleFactor // TODO: Theme!
bottomMargin: 56 * screenScaleFactor // TODO: Theme!
}
wrapMode: Text.WordWrap
text:
{
if (!printer.activePrintJob)
{
return ""
}
var topLine
if (materialsAreKnown(printer.activePrintJob))
{
topLine = catalog.i18ncp("@label", "The assigned printer, %1, requires the following configuration change:", "The assigned printer, %1, requires the following configuration changes:", printer.activePrintJob.configurationChanges.length).arg(printer.name)
}
else
{
topLine = catalog.i18nc("@label", "The printer %1 is assigned, but the job contains an unknown material configuration.").arg(printer.name)
}
var result = "<p>" + topLine +"</p>\n\n"
for (var i = 0; i < printer.activePrintJob.configurationChanges.length; i++)
{
var change = printer.activePrintJob.configurationChanges[i]
var text
switch (change.typeOfChange)
{
case "material_change":
text = catalog.i18nc("@label", "Change material %1 from %2 to %3.").arg(change.index + 1).arg(change.originName).arg(change.targetName)
break
case "material_insert":
text = catalog.i18nc("@label", "Load %3 as material %1 (This cannot be overridden).").arg(change.index + 1).arg(change.targetName)
break
case "print_core_change":
text = catalog.i18nc("@label", "Change print core %1 from %2 to %3.").arg(change.index + 1).arg(change.originName).arg(change.targetName)
break
case "buildplate_change":
text = catalog.i18nc("@label", "Change build plate to %1 (This cannot be overridden).").arg(formatBuildPlateType(change.target_name))
break
default:
text = "unknown"
}
result += "<p><b>" + text + "</b></p>\n\n"
}
var bottomLine = catalog.i18nc("@label", "Override will use the specified settings with the existing printer configuration. This may result in a failed print.")
result += "<p>" + bottomLine + "</p>\n\n"
return result
}
}
// Utils
function formatPrintJobName(name)
{
var extensions = [ ".gcode.gz", ".gz", ".gcode", ".ufp" ]
for (var i = 0; i < extensions.length; i++)
{
var extension = extensions[i]
if (name.slice(-extension.length) === extension)
{
name = name.substring(0, name.length - extension.length)
}
}
return name;
}
function materialsAreKnown(job)
{
var conf0 = job.configuration[0]
if (conf0 && !conf0.material.material)
{
return false
}
var conf1 = job.configuration[1]
if (conf1 && !conf1.material.material)
{
return false
}
return true
}
function formatBuildPlateType(buildPlateType)
{
var translationText = ""
switch (buildPlateType) {
case "glass":
translationText = catalog.i18nc("@label", "Glass")
break
case "aluminum":
translationText = catalog.i18nc("@label", "Aluminum")
break
default:
translationText = null
}
return translationText
}
}

View file

@ -49,7 +49,7 @@ Item
} }
color: "#191919" // TODO: Theme! color: "#191919" // TODO: Theme!
elide: Text.ElideRight elide: Text.ElideRight
font: UM.Theme.getFont("very_small") // 12pt, regular font: UM.Theme.getFont("default") // 12pt, regular
text: "" text: ""
// FIXED-LINE-HEIGHT: // FIXED-LINE-HEIGHT:
@ -66,7 +66,7 @@ Item
} }
color: "#191919" // TODO: Theme! color: "#191919" // TODO: Theme!
elide: Text.ElideRight elide: Text.ElideRight
font: UM.Theme.getFont("small") // 12pt, bold font: UM.Theme.getFont("default_bold") // 12pt, bold
text: "" text: ""
// FIXED-LINE-HEIGHT: // FIXED-LINE-HEIGHT:

View file

@ -26,6 +26,7 @@ Item
ExpandableCard ExpandableCard
{ {
borderColor: printJob.configurationChanges.length !== 0 ? "#f5a623" : "#EAEAEC" // TODO: Theme!
headerItem: Row headerItem: Row
{ {
height: 48 * screenScaleFactor // TODO: Theme! height: 48 * screenScaleFactor // TODO: Theme!

View file

@ -23,7 +23,7 @@ Item
anchors.fill: parent anchors.fill: parent
opacity: opacity:
{ {
if (printJob && (printJob.state == "error" || !printJob.isActive)) if (printJob && (printJob.state == "error" || printJob.configurationChanges.length > 0 || !printJob.isActive))
{ {
return 0.5 return 0.5
} }
@ -60,6 +60,14 @@ Item
height: 0.5 * printJobPreview.height height: 0.5 * printJobPreview.height
source: source:
{ {
if (!printJob)
{
return ""
}
if (printJob.configurationChanges.length > 0)
{
return "../svg/warning-icon.svg"
}
switch(printJob.state) switch(printJob.state)
{ {
case "error": case "error":
@ -75,6 +83,7 @@ Item
default: default:
return "" return ""
} }
return ""
} }
sourceSize sourceSize
{ {

View file

@ -55,7 +55,7 @@ Item
left: progressBar.right left: progressBar.right
leftMargin: 18 * screenScaleFactor // TODO: Theme! leftMargin: 18 * screenScaleFactor // TODO: Theme!
} }
text: Math.round(printJob.progress * 100) + "%" text: printJob ? Math.round(printJob.progress * 100) + "%" : "0%"
color: printJob && printJob.isActive ? "#374355" : "#babac1" // TODO: Theme! color: printJob && printJob.isActive ? "#374355" : "#babac1" // TODO: Theme!
width: contentWidth width: contentWidth
font: UM.Theme.getFont("medium") // 14pt, regular font: UM.Theme.getFont("medium") // 14pt, regular
@ -88,6 +88,8 @@ Item
return catalog.i18nc("@label:status", "Aborted") return catalog.i18nc("@label:status", "Aborted")
} }
return catalog.i18nc("@label:status", "Finished") return catalog.i18nc("@label:status", "Finished")
case "finished":
return catalog.i18nc("@label:status", "Finished")
case "sent_to_printer": case "sent_to_printer":
return catalog.i18nc("@label:status", "Preparing...") return catalog.i18nc("@label:status", "Preparing...")
case "pre_print": case "pre_print":

View file

@ -3,6 +3,7 @@
import QtQuick 2.3 import QtQuick 2.3
import QtQuick.Controls 2.0 import QtQuick.Controls 2.0
import QtQuick.Dialogs 1.1
import UM 1.3 as UM import UM 1.3 as UM
/** /**
@ -66,7 +67,7 @@ Item
{ {
verticalCenter: parent.verticalCenter verticalCenter: parent.verticalCenter
} }
width: 216 * screenScaleFactor // TODO: Theme! width: 180 * screenScaleFactor // TODO: Theme!
height: printerNameLabel.height + printerFamilyPill.height + 6 * screenScaleFactor // TODO: Theme! height: printerNameLabel.height + printerFamilyPill.height + 6 * screenScaleFactor // TODO: Theme!
Label Label
@ -150,7 +151,7 @@ Item
} }
border border
{ {
color: "#EAEAEC" // TODO: Theme! color: printer.activePrintJob && printer.activePrintJob.configurationChanges.length > 0 ? "#f5a623" : "#EAEAEC" // TODO: Theme!
width: borderSize // TODO: Remove once themed width: borderSize // TODO: Remove once themed
} }
color: "white" // TODO: Theme! color: "white" // TODO: Theme!
@ -185,14 +186,15 @@ Item
} }
if (printer && printer.state == "unreachable") if (printer && printer.state == "unreachable")
{ {
return catalog.i18nc("@label:status", "Unreachable") return catalog.i18nc("@label:status", "Unavailable")
} }
if (printer && printer.state == "idle") if (printer && !printer.activePrintJob && printer.state == "idle")
{ {
return catalog.i18nc("@label:status", "Idle") return catalog.i18nc("@label:status", "Idle")
} }
return "" return ""
} }
visible: text !== ""
} }
Item Item
@ -218,7 +220,7 @@ Item
{ {
verticalCenter: parent.verticalCenter verticalCenter: parent.verticalCenter
} }
width: 216 * screenScaleFactor // TODO: Theme! width: 180 * screenScaleFactor // TODO: Theme!
height: printerNameLabel.height + printerFamilyPill.height + 6 * screenScaleFactor // TODO: Theme! height: printerNameLabel.height + printerFamilyPill.height + 6 * screenScaleFactor // TODO: Theme!
visible: printer.activePrintJob visible: printer.activePrintJob
@ -247,7 +249,7 @@ Item
} }
color: printer.activePrintJob && printer.activePrintJob.isActive ? "#53657d" : "#babac1" // TODO: Theme! color: printer.activePrintJob && printer.activePrintJob.isActive ? "#53657d" : "#babac1" // TODO: Theme!
elide: Text.ElideRight elide: Text.ElideRight
font: UM.Theme.getFont("very_small") // 12pt, regular font: UM.Theme.getFont("default") // 12pt, regular
text: printer.activePrintJob ? printer.activePrintJob.owner : "Anonymous" // TODO: I18N text: printer.activePrintJob ? printer.activePrintJob.owner : "Anonymous" // TODO: I18N
width: parent.width width: parent.width
@ -264,8 +266,67 @@ Item
verticalCenter: parent.verticalCenter verticalCenter: parent.verticalCenter
} }
printJob: printer.activePrintJob printJob: printer.activePrintJob
visible: printer.activePrintJob visible: printer.activePrintJob && printer.activePrintJob.configurationChanges.length === 0
}
Label
{
anchors
{
verticalCenter: parent.verticalCenter
}
font: UM.Theme.getFont("default")
text: "Requires configuration changes"
visible: printer.activePrintJob && printer.activePrintJob.configurationChanges.length > 0
// FIXED-LINE-HEIGHT:
height: 18 * screenScaleFactor // TODO: Theme!
verticalAlignment: Text.AlignVCenter
} }
} }
Button
{
id: detailsButton
anchors
{
verticalCenter: parent.verticalCenter
right: parent.right
rightMargin: 18 * screenScaleFactor // TODO: Theme!
}
background: Rectangle
{
color: "#d8d8d8" // TODO: Theme!
radius: 2 * screenScaleFactor // Todo: Theme!
Rectangle
{
anchors.fill: parent
anchors.bottomMargin: 2 * screenScaleFactor // TODO: Theme!
color: detailsButton.hovered ? "#e4e4e4" : "#f0f0f0" // TODO: Theme!
radius: 2 * screenScaleFactor // Todo: Theme!
}
}
contentItem: Label
{
anchors.fill: parent
anchors.bottomMargin: 2 * screenScaleFactor // TODO: Theme!
color: "#1e66d7" // TODO: Theme!
font: UM.Theme.getFont("medium") // 14pt, regular
text: "Details" // TODO: I18NC!
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
height: 18 * screenScaleFactor // TODO: Theme!
}
implicitHeight: 32 * screenScaleFactor // TODO: Theme!
implicitWidth: 96 * screenScaleFactor // TODO: Theme!
visible: printer.activePrintJob && printer.activePrintJob.configurationChanges.length > 0
onClicked: overrideConfirmationDialog.open()
}
}
MonitorConfigOverrideDialog
{
id: overrideConfirmationDialog
printer: base.printer
} }
} }

View file

@ -8,6 +8,7 @@ import UM 1.3 as UM
import Cura 1.0 as Cura import Cura 1.0 as Cura
import QtGraphicalEffects 1.0 import QtGraphicalEffects 1.0
// Root component for the monitor tab (stage)
Component Component
{ {
Item Item
@ -130,7 +131,7 @@ Component
verticalCenter: externalLinkIcon.verticalCenter verticalCenter: externalLinkIcon.verticalCenter
} }
color: UM.Theme.getColor("primary") color: UM.Theme.getColor("primary")
font: UM.Theme.getFont("default") font: UM.Theme.getFont("default") // 12pt, regular
linkColor: UM.Theme.getColor("primary") linkColor: UM.Theme.getColor("primary")
text: catalog.i18nc("@label link to connect manager", "Manage queue in Cura Connect") text: catalog.i18nc("@label link to connect manager", "Manage queue in Cura Connect")
} }

View file

@ -1,121 +0,0 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.2
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import UM 1.2 as UM
Item {
id: extruderInfo;
property var printCoreConfiguration: null;
height: childrenRect.height;
width: Math.round(parent.width / 2);
// Extruder circle
Item {
id: extruderCircle;
height: UM.Theme.getSize("monitor_extruder_circle").height;
width: UM.Theme.getSize("monitor_extruder_circle").width;
// Loading skeleton
Rectangle {
anchors.fill: parent;
color: UM.Theme.getColor("monitor_skeleton_fill");
radius: Math.round(width / 2);
visible: !printCoreConfiguration;
}
// Actual content
Rectangle {
anchors.fill: parent;
border.width: UM.Theme.getSize("monitor_thick_lining").width;
border.color: UM.Theme.getColor("monitor_lining_heavy");
color: "transparent";
opacity: {
if (printCoreConfiguration == null || printCoreConfiguration.activeMaterial == null || printCoreConfiguration.hotendID == null) {
return 0.5;
}
return 1;
}
radius: Math.round(width / 2);
visible: printCoreConfiguration;
Label {
anchors.centerIn: parent;
color: UM.Theme.getColor("text");
font: UM.Theme.getFont("default_bold");
text: printCoreConfiguration ? printCoreConfiguration.position + 1 : 0;
}
}
}
// Print core and material labels
Item {
id: materialLabel
anchors {
left: extruderCircle.right;
leftMargin: UM.Theme.getSize("default_margin").width;
right: parent.right;
top: parent.top;
}
height: UM.Theme.getSize("monitor_text_line").height;
// Loading skeleton
Rectangle {
anchors.fill: parent;
color: UM.Theme.getColor("monitor_skeleton_fill");
visible: !extruderInfo.printCoreConfiguration;
}
// Actual content
Label {
anchors.fill: parent;
elide: Text.ElideRight;
color: UM.Theme.getColor("text");
font: UM.Theme.getFont("default");
text: {
if (printCoreConfiguration && printCoreConfiguration.activeMaterial != undefined) {
return printCoreConfiguration.activeMaterial.name;
}
return "";
}
visible: extruderInfo.printCoreConfiguration;
}
}
Item {
id: printCoreLabel;
anchors {
left: extruderCircle.right;
leftMargin: UM.Theme.getSize("default_margin").width;
right: parent.right;
top: materialLabel.bottom;
topMargin: Math.floor(UM.Theme.getSize("default_margin").height/4);
}
height: UM.Theme.getSize("monitor_text_line").height;
// Loading skeleton
Rectangle {
color: UM.Theme.getColor("monitor_skeleton_fill");
height: parent.height;
visible: !extruderInfo.printCoreConfiguration;
width: Math.round(parent.width / 3);
}
// Actual content
Label {
color: UM.Theme.getColor("text");
elide: Text.ElideRight;
font: UM.Theme.getFont("default");
opacity: 0.6;
text: {
if (printCoreConfiguration != undefined && printCoreConfiguration.hotendID != undefined) {
return printCoreConfiguration.hotendID;
}
return "";
}
visible: extruderInfo.printCoreConfiguration;
}
}
}

View file

@ -182,7 +182,7 @@ Item {
abortConfirmationDialog.visible = true; abortConfirmationDialog.visible = true;
popup.close(); popup.close();
} }
text: printJob.state == "aborting" ? catalog.i18nc("@label", "Aborting...") : catalog.i18nc("@label", "Abort"); text: printJob && printJob.state == "aborting" ? catalog.i18nc("@label", "Aborting...") : catalog.i18nc("@label", "Abort");
visible: { visible: {
if (!printJob) { if (!printJob) {
return false; return false;

View file

@ -1,505 +0,0 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.2
import QtQuick.Dialogs 1.1
import QtQuick.Controls 2.0
import QtQuick.Controls.Styles 1.4
import QtGraphicalEffects 1.0
import QtQuick.Layouts 1.1
import QtQuick.Dialogs 1.1
import UM 1.3 as UM
Item {
id: root;
property var shadowRadius: UM.Theme.getSize("monitor_shadow_radius").width;
property var shadowOffset: 2 * screenScaleFactor;
property var debug: false;
property var printJob: null;
width: parent.width; // Bubbles downward
height: childrenRect.height + shadowRadius * 2; // Bubbles upward
UM.I18nCatalog {
id: catalog;
name: "cura";
}
// The actual card (white block)
Rectangle {
// 5px margin, but shifted 2px vertically because of the shadow
anchors {
bottomMargin: root.shadowRadius + root.shadowOffset;
leftMargin: root.shadowRadius;
rightMargin: root.shadowRadius;
topMargin: root.shadowRadius - root.shadowOffset;
}
color: UM.Theme.getColor("monitor_card_background");
height: childrenRect.height;
layer.enabled: true
layer.effect: DropShadow {
radius: root.shadowRadius
verticalOffset: 2 * screenScaleFactor
color: "#3F000000" // 25% shadow
}
width: parent.width - shadowRadius * 2;
Column {
height: childrenRect.height;
width: parent.width;
// Main content
Item {
id: mainContent;
height: 200 * screenScaleFactor; // TODO: Theme!
width: parent.width;
// Left content
Item {
anchors {
bottom: parent.bottom;
left: parent.left;
margins: UM.Theme.getSize("wide_margin").width;
right: parent.horizontalCenter;
top: parent.top;
}
Item {
id: printJobName;
width: parent.width;
height: UM.Theme.getSize("monitor_text_line").height;
Rectangle {
color: UM.Theme.getColor("monitor_skeleton_fill");
height: parent.height;
visible: !printJob;
width: Math.round(parent.width / 3);
}
Label {
anchors.fill: parent;
color: UM.Theme.getColor("text");
elide: Text.ElideRight;
font: UM.Theme.getFont("default_bold");
text: printJob && printJob.name ? printJob.name : ""; // Supress QML warnings
visible: printJob;
}
}
Item {
id: printJobOwnerName;
anchors {
top: printJobName.bottom;
topMargin: Math.floor(UM.Theme.getSize("default_margin").height / 2);
}
height: UM.Theme.getSize("monitor_text_line").height;
width: parent.width;
Rectangle {
color: UM.Theme.getColor("monitor_skeleton_fill");
height: parent.height;
visible: !printJob;
width: Math.round(parent.width / 2);
}
Label {
anchors.fill: parent;
color: UM.Theme.getColor("text");
elide: Text.ElideRight;
font: UM.Theme.getFont("default");
text: printJob ? printJob.owner : ""; // Supress QML warnings
visible: printJob;
}
}
Item {
id: printJobPreview;
property var useUltibot: false;
anchors {
bottom: parent.bottom;
horizontalCenter: parent.horizontalCenter;
top: printJobOwnerName.bottom;
topMargin: UM.Theme.getSize("default_margin").height;
}
width: height;
// Skeleton
Rectangle {
anchors.fill: parent;
color: UM.Theme.getColor("monitor_skeleton_fill");
radius: UM.Theme.getSize("default_margin").width;
visible: !printJob;
}
// Actual content
Image {
id: previewImage;
anchors.fill: parent;
opacity: printJob && printJob.state == "error" ? 0.5 : 1.0;
source: printJob ? printJob.previewImageUrl : "";
visible: printJob;
}
UM.RecolorImage {
id: ultiBotImage;
anchors.centerIn: printJobPreview;
color: UM.Theme.getColor("monitor_placeholder_image");
height: printJobPreview.height;
source: "../svg/ultibot.svg";
sourceSize {
height: height;
width: width;
}
/* Since print jobs ALWAYS have an image url, we have to check if that image URL errors or
not in order to determine if we show the placeholder (ultibot) image instead. */
visible: printJob && previewImage.status == Image.Error;
width: printJobPreview.width;
}
UM.RecolorImage {
id: statusImage;
anchors.centerIn: printJobPreview;
color: UM.Theme.getColor("monitor_image_overlay");
height: 0.5 * printJobPreview.height;
source: printJob && printJob.state == "error" ? "../svg/aborted-icon.svg" : "";
sourceSize {
height: height;
width: width;
}
visible: source != "";
width: 0.5 * printJobPreview.width;
}
}
Label {
id: totalTimeLabel;
anchors {
bottom: parent.bottom;
right: parent.right;
}
color: UM.Theme.getColor("text");
elide: Text.ElideRight;
font: UM.Theme.getFont("default");
text: printJob ? OutputDevice.formatDuration(printJob.timeTotal) : "";
}
}
// Divider
Rectangle {
anchors {
horizontalCenter: parent.horizontalCenter;
verticalCenter: parent.verticalCenter;
}
color: !printJob ? UM.Theme.getColor("monitor_skeleton_fill") : UM.Theme.getColor("monitor_lining_light");
height: parent.height - 2 * UM.Theme.getSize("default_margin").height;
width: UM.Theme.getSize("default_lining").width;
}
// Right content
Item {
anchors {
bottom: parent.bottom;
left: parent.horizontalCenter;
margins: UM.Theme.getSize("wide_margin").width;
right: parent.right;
top: parent.top;
}
Item {
id: targetPrinterLabel;
height: UM.Theme.getSize("monitor_text_line").height;
width: parent.width;
Rectangle {
visible: !printJob;
color: UM.Theme.getColor("monitor_skeleton_fill");
anchors.fill: parent;
}
Label {
color: UM.Theme.getColor("text");
elide: Text.ElideRight;
font: UM.Theme.getFont("default_bold");
text: {
if (printJob !== null) {
if (printJob.assignedPrinter == null) {
if (printJob.state == "error") {
return catalog.i18nc("@label", "Waiting for: Unavailable printer");
}
return catalog.i18nc("@label", "Waiting for: First available");
} else {
return catalog.i18nc("@label", "Waiting for: ") + printJob.assignedPrinter.name;
}
}
return "";
}
visible: printJob;
}
}
PrinterInfoBlock {
anchors.bottom: parent.bottom;
printer: root.printJon && root.printJob.assignedPrinter;
printJob: root.printJob;
}
}
PrintJobContextMenu {
id: contextButton;
anchors {
right: mainContent.right;
rightMargin: UM.Theme.getSize("default_margin").width * 3 + root.shadowRadius;
top: mainContent.top;
topMargin: UM.Theme.getSize("default_margin").height;
}
printJob: root.printJob;
visible: root.printJob;
}
}
Item {
id: configChangesBox;
height: childrenRect.height;
visible: printJob && printJob.configurationChanges.length !== 0;
width: parent.width;
// Config change toggle
Rectangle {
id: configChangeToggle;
color: {
if (configChangeToggleArea.containsMouse) {
return UM.Theme.getColor("viewport_background"); // TODO: Theme!
} else {
return "transparent";
}
}
width: parent.width;
height: UM.Theme.getSize("default_margin").height * 4; // TODO: Theme!
anchors {
left: parent.left;
right: parent.right;
top: parent.top;
}
Rectangle {
color: !printJob ? UM.Theme.getColor("monitor_skeleton_fill") : UM.Theme.getColor("monitor_lining_light");
height: UM.Theme.getSize("default_lining").height;
width: parent.width;
}
UM.RecolorImage {
anchors {
right: configChangeToggleLabel.left;
rightMargin: UM.Theme.getSize("default_margin").width;
verticalCenter: parent.verticalCenter;
}
color: UM.Theme.getColor("text");
height: 23 * screenScaleFactor; // TODO: Theme!
source: "../svg/warning-icon.svg";
sourceSize {
height: height;
width: width;
}
width: 23 * screenScaleFactor; // TODO: Theme!
}
Label {
id: configChangeToggleLabel;
anchors {
horizontalCenter: parent.horizontalCenter;
verticalCenter: parent.verticalCenter;
}
color: UM.Theme.getColor("text");
font: UM.Theme.getFont("default");
text: catalog.i18nc("@label", "Configuration change");
}
UM.RecolorImage {
anchors {
left: configChangeToggleLabel.right;
leftMargin: UM.Theme.getSize("default_margin").width;
verticalCenter: parent.verticalCenter;
}
color: UM.Theme.getColor("text");
height: 15 * screenScaleFactor; // TODO: Theme!
source: {
if (configChangeDetails.visible) {
return UM.Theme.getIcon("arrow_top");
} else {
return UM.Theme.getIcon("arrow_bottom");
}
}
sourceSize {
width: width;
height: height;
}
width: 15 * screenScaleFactor; // TODO: Theme!
}
MouseArea {
id: configChangeToggleArea;
anchors.fill: parent;
hoverEnabled: true;
onClicked: {
configChangeDetails.visible = !configChangeDetails.visible;
}
}
}
// Config change details
Item {
id: configChangeDetails;
anchors.top: configChangeToggle.bottom;
Behavior on height { NumberAnimation { duration: 100 } }
// In case of really massive multi-line configuration changes
height: visible ? Math.max(UM.Theme.getSize("monitor_config_override_box").height, childrenRect.height) : 0;
visible: false;
width: parent.width;
Item {
anchors {
bottomMargin: UM.Theme.getSize("wide_margin").height;
fill: parent;
leftMargin: UM.Theme.getSize("wide_margin").height * 4;
rightMargin: UM.Theme.getSize("wide_margin").height * 4;
topMargin: UM.Theme.getSize("wide_margin").height;
}
clip: true;
Label {
anchors.fill: parent;
elide: Text.ElideRight;
color: UM.Theme.getColor("text");
font: UM.Theme.getFont("default");
text: {
if (!printJob || printJob.configurationChanges.length === 0) {
return "";
}
var topLine;
if (materialsAreKnown(printJob)) {
topLine = catalog.i18nc("@label", "The assigned printer, %1, requires the following configuration change(s):").arg(printJob.assignedPrinter.name);
} else {
topLine = catalog.i18nc("@label", "The printer %1 is assigned, but the job contains an unknown material configuration.").arg(printJob.assignedPrinter.name);
}
var result = "<p>" + topLine +"</p>";
for (var i = 0; i < printJob.configurationChanges.length; i++) {
var change = printJob.configurationChanges[i];
var text;
switch (change.typeOfChange) {
case "material_change":
text = catalog.i18nc("@label", "Change material %1 from %2 to %3.").arg(change.index + 1).arg(change.originName).arg(change.targetName);
break;
case "material_insert":
text = catalog.i18nc("@label", "Load %3 as material %1 (This cannot be overridden).").arg(change.index + 1).arg(change.targetName);
break;
case "print_core_change":
text = catalog.i18nc("@label", "Change print core %1 from %2 to %3.").arg(change.index + 1).arg(change.originName).arg(change.targetName);
break;
case "buildplate_change":
text = catalog.i18nc("@label", "Change build plate to %1 (This cannot be overridden).").arg(formatBuildPlateType(change.target_name));
break;
default:
text = "";
}
result += "<p><b>" + text + "</b></p>";
}
return result;
}
wrapMode: Text.WordWrap;
}
Button {
anchors {
bottom: parent.bottom;
left: parent.left;
}
background: Rectangle {
border {
color: UM.Theme.getColor("monitor_lining_heavy");
width: UM.Theme.getSize("default_lining").width;
}
color: parent.hovered ? UM.Theme.getColor("monitor_card_background_inactive") : UM.Theme.getColor("monitor_card_background");
implicitHeight: UM.Theme.getSize("default_margin").height * 3;
implicitWidth: UM.Theme.getSize("default_margin").height * 8;
}
contentItem: Label {
color: UM.Theme.getColor("text");
font: UM.Theme.getFont("medium");
horizontalAlignment: Text.AlignHCenter;
text: parent.text;
verticalAlignment: Text.AlignVCenter;
}
onClicked: {
overrideConfirmationDialog.visible = true;
}
text: catalog.i18nc("@label", "Override");
visible: {
if (printJob && printJob.configurationChanges) {
var length = printJob.configurationChanges.length;
for (var i = 0; i < length; i++) {
var typeOfChange = printJob.configurationChanges[i].typeOfChange;
if (typeOfChange === "material_insert" || typeOfChange === "buildplate_change") {
return false;
}
}
}
return true;
}
}
}
}
MessageDialog {
id: overrideConfirmationDialog;
Component.onCompleted: visible = false;
icon: StandardIcon.Warning;
onYes: OutputDevice.forceSendJob(printJob.key);
standardButtons: StandardButton.Yes | StandardButton.No;
text: {
if (!printJob) {
return "";
}
var printJobName = formatPrintJobName(printJob.name);
var confirmText = catalog.i18nc("@label", "Starting a print job with an incompatible configuration could damage your 3D printer. Are you sure you want to override the configuration and print %1?").arg(printJobName);
return confirmText;
}
title: catalog.i18nc("@window:title", "Override configuration configuration and start print");
}
}
}
}
// Utils
function formatPrintJobName(name) {
var extensions = [ ".gz", ".gcode", ".ufp" ];
for (var i = 0; i < extensions.length; i++) {
var extension = extensions[i];
if (name.slice(-extension.length) === extension) {
name = name.substring(0, name.length - extension.length);
}
}
return name;
}
function materialsAreKnown(job) {
var conf0 = job.configuration[0];
if (conf0 && !conf0.material.material) {
return false;
}
var conf1 = job.configuration[1];
if (conf1 && !conf1.material.material) {
return false;
}
return true;
}
function formatBuildPlateType(buildPlateType) {
var translationText = "";
switch (buildPlateType) {
case "glass":
translationText = catalog.i18nc("@label", "Glass");
break;
case "aluminum":
translationText = catalog.i18nc("@label", "Aluminum");
break;
default:
translationText = null;
}
return translationText;
}
}

View file

@ -1,75 +0,0 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.3
import QtQuick.Dialogs 1.1
import QtQuick.Controls 2.0
import QtQuick.Controls.Styles 1.3
import QtGraphicalEffects 1.0
import QtQuick.Controls 1.4 as LegacyControls
import UM 1.3 as UM
// Includes print job name, owner, and preview
Item {
property var job: null;
property var useUltibot: false;
height: 100 * screenScaleFactor;
width: height;
// Skeleton
Rectangle {
anchors.fill: parent;
color: UM.Theme.getColor("monitor_skeleton_fill");
radius: UM.Theme.getSize("default_margin").width;
visible: !job;
}
// Actual content
Image {
id: previewImage;
visible: job;
source: job ? job.previewImageUrl : "";
opacity: {
if (job == null) {
return 1.0;
}
var states = ["wait_cleanup", "wait_user_action", "error", "paused"];
if (states.indexOf(job.state) !== -1) {
return 0.5;
}
return 1.0;
}
anchors.fill: parent;
}
UM.RecolorImage {
id: ultibotImage;
anchors.centerIn: parent;
color: UM.Theme.getColor("monitor_placeholder_image"); // TODO: Theme!
height: parent.height;
source: "../svg/ultibot.svg";
sourceSize {
height: height;
width: width;
}
/* Since print jobs ALWAYS have an image url, we have to check if that image URL errors or
not in order to determine if we show the placeholder (ultibot) image instead. */
visible: job && previewImage.status == Image.Error;
width: parent.width;
}
UM.RecolorImage {
id: statusImage;
anchors.centerIn: parent;
color: "black"; // TODO: Theme!
height: Math.round(0.5 * parent.height);
source: job && job.state == "error" ? "../svg/aborted-icon.svg" : "";
sourceSize {
height: height;
width: width;
}
visible: source != "";
width: Math.round(0.5 * parent.width);
}
}

View file

@ -1,59 +0,0 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.3
import QtQuick.Controls 2.0
import UM 1.3 as UM
Column {
property var job: null;
height: childrenRect.height;
spacing: Math.floor( UM.Theme.getSize("default_margin").height / 2); // TODO: Use explicit theme size
width: parent.width;
Item {
id: jobName;
height: UM.Theme.getSize("monitor_text_line").height;
width: parent.width;
// Skeleton loading
Rectangle {
color: UM.Theme.getColor("monitor_skeleton_fill");
height: parent.height;
visible: !job;
width: Math.round(parent.width / 3);
}
Label {
anchors.fill: parent;
color: UM.Theme.getColor("text");
elide: Text.ElideRight;
font: UM.Theme.getFont("default_bold");
text: job && job.name ? job.name : "";
visible: job;
}
}
Item {
id: ownerName;
height: UM.Theme.getSize("monitor_text_line").height;
width: parent.width;
// Skeleton loading
Rectangle {
color: UM.Theme.getColor("monitor_skeleton_fill");
height: parent.height;
visible: !job;
width: Math.round(parent.width / 2);
}
Label {
anchors.fill: parent;
color: UM.Theme.getColor("text")
elide: Text.ElideRight;
font: UM.Theme.getFont("default");
text: job ? job.owner : "";
visible: job;
}
}
}

View file

@ -1,241 +0,0 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.3
import QtQuick.Dialogs 1.1
import QtQuick.Controls 2.0
import QtQuick.Controls.Styles 1.3
import QtGraphicalEffects 1.0
import UM 1.3 as UM
Item {
id: root;
property var shadowRadius: UM.Theme.getSize("monitor_shadow_radius").width;
property var shadowOffset: UM.Theme.getSize("monitor_shadow_offset").width;
property var printer: null;
property var collapsed: true;
height: childrenRect.height + shadowRadius * 2; // Bubbles upward
width: parent.width; // Bubbles downward
// The actual card (white block)
Rectangle {
// 5px margin, but shifted 2px vertically because of the shadow
anchors {
bottomMargin: root.shadowRadius + root.shadowOffset;
leftMargin: root.shadowRadius;
rightMargin: root.shadowRadius;
topMargin: root.shadowRadius - root.shadowOffset;
}
color: {
if (!printer) {
return UM.Theme.getColor("monitor_card_background_inactive");
}
if (printer.state == "disabled") {
return UM.Theme.getColor("monitor_card_background_inactive");
} else {
return UM.Theme.getColor("monitor_card_background");
}
}
height: childrenRect.height;
layer.effect: DropShadow {
radius: root.shadowRadius;
verticalOffset: root.shadowOffset;
color: "#3F000000"; // 25% shadow
}
layer.enabled: true
width: parent.width - 2 * shadowRadius;
Column {
id: cardContents;
height: childrenRect.height;
width: parent.width;
// Main card
Item {
id: mainCard;
anchors {
left: parent.left;
leftMargin: UM.Theme.getSize("default_margin").width;
right: parent.right;
rightMargin: UM.Theme.getSize("default_margin").width;
}
height: 60 * screenScaleFactor + 2 * UM.Theme.getSize("default_margin").height;
width: parent.width;
// Machine icon
Item {
id: machineIcon;
anchors.verticalCenter: parent.verticalCenter;
height: parent.height - 2 * UM.Theme.getSize("default_margin").width;
width: height;
// Skeleton
Rectangle {
anchors.fill: parent;
color: UM.Theme.getColor("monitor_skeleton_fill_dark");
radius: UM.Theme.getSize("default_margin").width;
visible: !printer;
}
// Content
UM.RecolorImage {
anchors.centerIn: parent;
color: {
if (printer && printer.activePrintJob != undefined) {
return UM.Theme.getColor("monitor_printer_icon");
}
return UM.Theme.getColor("monitor_printer_icon_inactive");
}
height: sourceSize.height;
source: {
if (!printer) {
return "";
}
switch(printer.type) {
case "Ultimaker 3":
return "../svg/UM3-icon.svg";
case "Ultimaker 3 Extended":
return "../svg/UM3x-icon.svg";
case "Ultimaker S5":
return "../svg/UMs5-icon.svg";
}
}
visible: printer;
width: sourceSize.width;
}
}
// Printer info
Item {
id: printerInfo;
anchors {
left: machineIcon.right;
leftMargin: UM.Theme.getSize("wide_margin").width;
right: collapseIcon.left;
verticalCenter: machineIcon.verticalCenter;
}
height: childrenRect.height;
// Machine name
Item {
id: machineNameLabel;
height: UM.Theme.getSize("monitor_text_line").height;
width: {
var percent = printer ? 0.75 : 0.3;
return Math.round(parent.width * percent);
}
// Skeleton
Rectangle {
anchors.fill: parent;
color: UM.Theme.getColor("monitor_skeleton_fill_dark");
visible: !printer;
}
// Actual content
Label {
anchors.fill: parent;
color: UM.Theme.getColor("text");
elide: Text.ElideRight;
font: UM.Theme.getFont("default_bold");
text: printer ? printer.name : "";
visible: printer;
width: parent.width;
}
}
// Job name
Item {
id: activeJobLabel;
anchors {
top: machineNameLabel.bottom;
topMargin: Math.round(UM.Theme.getSize("default_margin").height / 2);
}
height: UM.Theme.getSize("monitor_text_line").height;
width: Math.round(parent.width * 0.75);
// Skeleton
Rectangle {
anchors.fill: parent;
color: UM.Theme.getColor("monitor_skeleton_fill_dark");
visible: !printer;
}
// Actual content
Label {
anchors.fill: parent;
color: UM.Theme.getColor("monitor_text_inactive");
elide: Text.ElideRight;
font: UM.Theme.getFont("default");
text: {
if (!printer) {
return "";
}
if (printer.state == "disabled") {
return catalog.i18nc("@label", "Not available");
} else if (printer.state == "unreachable") {
return catalog.i18nc("@label", "Unreachable");
}
if (printer.activePrintJob != null && printer.activePrintJob.name) {
return printer.activePrintJob.name;
}
return catalog.i18nc("@label", "Available");
}
visible: printer;
}
}
}
// Collapse icon
UM.RecolorImage {
id: collapseIcon;
anchors {
right: parent.right;
rightMargin: UM.Theme.getSize("default_margin").width;
verticalCenter: parent.verticalCenter;
}
color: UM.Theme.getColor("text");
height: 15 * screenScaleFactor; // TODO: Theme!
source: root.collapsed ? UM.Theme.getIcon("arrow_left") : UM.Theme.getIcon("arrow_bottom");
sourceSize {
height: height;
width: width;
}
visible: printer;
width: 15 * screenScaleFactor; // TODO: Theme!
}
MouseArea {
anchors.fill: parent;
enabled: printer;
onClicked: {
if (model && root.collapsed) {
printerList.currentIndex = model.index;
} else {
printerList.currentIndex = -1;
}
}
}
Connections {
target: printerList;
onCurrentIndexChanged: {
root.collapsed = printerList.currentIndex != model.index;
}
}
}
// Detailed card
PrinterCardDetails {
collapsed: root.collapsed;
printer: root.printer;
visible: root.printer;
}
// Progress bar
PrinterCardProgressBar {
visible: printer && printer.activePrintJob != null;
width: parent.width;
}
}
}
}

View file

@ -1,75 +0,0 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.3
import QtQuick.Dialogs 1.1
import QtQuick.Controls 2.0
import QtQuick.Controls.Styles 1.3
import QtGraphicalEffects 1.0
import QtQuick.Controls 1.4 as LegacyControls
import UM 1.3 as UM
Item {
id: root;
property var printer: null;
property var printJob: printer ? printer.activePrintJob : null;
property var collapsed: true;
Behavior on height { NumberAnimation { duration: 100 } }
Behavior on opacity { NumberAnimation { duration: 100 } }
height: collapsed ? 0 : childrenRect.height;
opacity: collapsed ? 0 : 1;
width: parent.width;
Column {
id: contentColumn;
anchors {
left: parent.left;
leftMargin: UM.Theme.getSize("default_margin").width;
right: parent.right;
rightMargin: UM.Theme.getSize("default_margin").width;
}
height: childrenRect.height + UM.Theme.getSize("default_margin").height;
spacing: UM.Theme.getSize("default_margin").height;
width: parent.width;
HorizontalLine {}
PrinterInfoBlock {
printer: root.printer;
printJob: root.printer ? root.printer.activePrintJob : null;
}
HorizontalLine {}
Row {
height: childrenRect.height;
visible: root.printJob;
width: parent.width;
PrintJobTitle {
job: root.printer ? root.printer.activePrintJob : null;
}
PrintJobContextMenu {
id: contextButton;
anchors {
right: parent.right;
rightMargin: UM.Theme.getSize("wide_margin").width;
}
printJob: root.printer ? root.printer.activePrintJob : null;
visible: printJob;
}
}
PrintJobPreview {
anchors.horizontalCenter: parent.horizontalCenter;
job: root.printer && root.printer.activePrintJob ? root.printer.activePrintJob : null;
visible: root.printJob;
}
CameraButton {
id: showCameraButton;
iconSource: "../svg/camera-icon.svg";
visible: root.printer;
}
}
}

View file

@ -1,108 +0,0 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.3
import QtQuick.Controls.Styles 1.3
import QtQuick.Controls 1.4
import UM 1.3 as UM
ProgressBar {
property var progress: {
if (!printer || printer.activePrintJob == null) {
return 0;
}
var result = printer.activePrintJob.timeElapsed / printer.activePrintJob.timeTotal;
if (result > 1.0) {
result = 1.0;
}
return result;
}
style: ProgressBarStyle {
property var remainingTime: {
if (!printer || printer.activePrintJob == null) {
return 0;
}
/* Sometimes total minus elapsed is less than 0. Use Math.max() to prevent remaining
time from ever being less than 0. Negative durations cause strange behavior such
as displaying "-1h -1m". */
return Math.max(printer.activePrintJob.timeTotal - printer.activePrintJob.timeElapsed, 0);
}
property var progressText: {
if (printer === null ) {
return "";
}
switch (printer.activePrintJob.state) {
case "wait_cleanup":
if (printer.activePrintJob.timeTotal > printer.activePrintJob.timeElapsed) {
return catalog.i18nc("@label:status", "Aborted");
}
return catalog.i18nc("@label:status", "Finished");
case "pre_print":
case "sent_to_printer":
return catalog.i18nc("@label:status", "Preparing");
case "aborted":
return catalog.i18nc("@label:status", "Aborted");
case "wait_user_action":
return catalog.i18nc("@label:status", "Aborted");
case "pausing":
return catalog.i18nc("@label:status", "Pausing");
case "paused":
return OutputDevice.formatDuration( remainingTime );
case "resuming":
return catalog.i18nc("@label:status", "Resuming");
case "queued":
return catalog.i18nc("@label:status", "Action required");
default:
return OutputDevice.formatDuration( remainingTime );
}
}
background: Rectangle {
color: UM.Theme.getColor("monitor_progress_background");
implicitHeight: visible ? 24 : 0;
implicitWidth: 100;
}
progress: Rectangle {
id: progressItem;
color: {
if (! printer || !printer.activePrintJob) {
return "black";
}
var state = printer.activePrintJob.state
var inactiveStates = [
"pausing",
"paused",
"resuming",
"wait_cleanup"
];
if (inactiveStates.indexOf(state) > -1 && remainingTime > 0) {
return UM.Theme.getColor("monitor_progress_fill_inactive");
} else {
return UM.Theme.getColor("monitor_progress_fill");
}
}
Label {
id: progressLabel;
anchors {
left: parent.left;
leftMargin: getTextOffset();
}
text: progressText;
anchors.verticalCenter: parent.verticalCenter;
color: progressItem.width + progressLabel.width < control.width ? UM.Theme.getColor("text") : UM.Theme.getColor("monitor_progress_fill_text");
width: contentWidth;
font: UM.Theme.getFont("default");
}
function getTextOffset() {
if (progressItem.width + progressLabel.width + 16 < control.width) {
return progressItem.width + UM.Theme.getSize("default_margin").width;
} else {
return progressItem.width - progressLabel.width - UM.Theme.getSize("default_margin").width;
}
}
}
}
value: progress;
width: parent.width;
}

View file

@ -1,32 +0,0 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.2
import QtQuick.Controls 1.4
import UM 1.2 as UM
Item {
property alias text: familyNameLabel.text;
property var padding: 3 * screenScaleFactor; // TODO: Theme!
implicitHeight: familyNameLabel.contentHeight + 2 * padding; // Apply the padding to top and bottom.
implicitWidth: Math.max(48 * screenScaleFactor, familyNameLabel.contentWidth + implicitHeight); // The extra height is added to ensure the radius doesn't cut something off.
Rectangle {
id: background;
anchors {
horizontalCenter: parent.horizontalCenter;
right: parent.right;
}
color: familyNameLabel.text.length < 1 ? UM.Theme.getColor("monitor_skeleton_fill") : UM.Theme.getColor("monitor_pill_background");
height: parent.height;
radius: 0.5 * height;
width: parent.width;
}
Label {
id: familyNameLabel;
anchors.centerIn: parent;
color: UM.Theme.getColor("text");
text: "";
}
}

View file

@ -1,83 +0,0 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.3
import QtQuick.Dialogs 1.1
import QtQuick.Controls 2.0
import QtQuick.Controls.Styles 1.3
import QtGraphicalEffects 1.0
import QtQuick.Controls 1.4 as LegacyControls
import UM 1.3 as UM
// Includes printer type pill and extuder configurations
Item {
id: root;
property var printer: null;
property var printJob: null;
width: parent.width;
height: childrenRect.height;
// Printer family pills
Row {
id: printerFamilyPills;
anchors {
left: parent.left;
right: parent.right;
}
height: childrenRect.height;
spacing: Math.round(0.5 * UM.Theme.getSize("default_margin").width);
width: parent.width;
Repeater {
id: compatiblePills;
delegate: PrinterFamilyPill {
text: modelData;
}
model: printJob ? printJob.compatibleMachineFamilies : [];
visible: printJob;
}
PrinterFamilyPill {
text: printer ? printer.type : "";
visible: !compatiblePills.visible && printer;
}
}
// Extruder info
Row {
id: extrudersInfo;
anchors {
left: parent.left;
right: parent.right;
rightMargin: UM.Theme.getSize("default_margin").width;
top: printerFamilyPills.bottom;
topMargin: UM.Theme.getSize("default_margin").height;
}
height: childrenRect.height;
spacing: UM.Theme.getSize("default_margin").width;
width: parent.width;
PrintCoreConfiguration {
width: Math.round(parent.width / 2) * screenScaleFactor;
printCoreConfiguration: getExtruderConfig(0);
}
PrintCoreConfiguration {
width: Math.round(parent.width / 2) * screenScaleFactor;
printCoreConfiguration: getExtruderConfig(1);
}
}
function getExtruderConfig( i ) {
if (root.printJob) {
// Use more-specific print job if possible
return root.printJob.configuration.extruderConfigurations[i];
}
if (root.printer) {
return root.printer.printerConfiguration.extruderConfigurations[i];
}
return null;
}
}

View file

@ -29,7 +29,7 @@ Item {
Button { Button {
height: UM.Theme.getSize("save_button_save_to_button").height; height: UM.Theme.getSize("save_button_save_to_button").height;
onClicked: Cura.MachineManager.printerOutputDevices[0].requestAuthentication(); onClicked: Cura.MachineManager.printerOutputDevices[0].requestAuthentication();
style: UM.Theme.styles.sidebar_action_button; style: UM.Theme.styles.print_setup_action_button;
text: catalog.i18nc("@action:button", "Request Access"); text: catalog.i18nc("@action:button", "Request Access");
tooltip: catalog.i18nc("@info:tooltip", "Send access request to the printer"); tooltip: catalog.i18nc("@info:tooltip", "Send access request to the printer");
visible: printerConnected && !printerAcceptsCommands && !authenticationRequested; visible: printerConnected && !printerAcceptsCommands && !authenticationRequested;
@ -38,7 +38,7 @@ Item {
Button { Button {
height: UM.Theme.getSize("save_button_save_to_button").height; height: UM.Theme.getSize("save_button_save_to_button").height;
onClicked: connectActionDialog.show(); onClicked: connectActionDialog.show();
style: UM.Theme.styles.sidebar_action_button; style: UM.Theme.styles.print_setup_action_button;
text: catalog.i18nc("@action:button", "Connect"); text: catalog.i18nc("@action:button", "Connect");
tooltip: catalog.i18nc("@info:tooltip", "Connect to a printer"); tooltip: catalog.i18nc("@info:tooltip", "Connect to a printer");
visible: !printerConnected; visible: !printerConnected;

View file

@ -64,7 +64,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
self._print_jobs = [] # type: List[UM3PrintJobOutputModel] self._print_jobs = [] # type: List[UM3PrintJobOutputModel]
self._received_print_jobs = False # type: bool self._received_print_jobs = False # type: bool
self._monitor_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../resources/qml/ClusterMonitorItem.qml") self._monitor_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../resources/qml/MonitorStage.qml")
# See comments about this hack with the clusterPrintersChanged signal # See comments about this hack with the clusterPrintersChanged signal
self.printersChanged.connect(self.clusterPrintersChanged) self.printersChanged.connect(self.clusterPrintersChanged)
@ -607,17 +607,24 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
def _createMaterialOutputModel(self, material_data: Dict[str, Any]) -> "MaterialOutputModel": def _createMaterialOutputModel(self, material_data: Dict[str, Any]) -> "MaterialOutputModel":
material_manager = CuraApplication.getInstance().getMaterialManager() material_manager = CuraApplication.getInstance().getMaterialManager()
material_group_list = material_manager.getMaterialGroupListByGUID(material_data["guid"]) material_group_list = None
# This can happen if the connected machine has no material in one or more extruders (if GUID is empty), or the
# material is unknown to Cura, so we should return an "empty" or "unknown" material model. # Avoid crashing if there is no "guid" field in the metadata
material_guid = material_data.get("guid")
if material_guid:
material_group_list = material_manager.getMaterialGroupListByGUID(material_guid)
# This can happen if the connected machine has no material in one or more extruders (if GUID is empty), or the
# material is unknown to Cura, so we should return an "empty" or "unknown" material model.
if material_group_list is None: if material_group_list is None:
material_name = "Empty" if len(material_data["guid"]) == 0 else "Unknown" material_name = i18n_catalog.i18nc("@label:material", "Empty") if len(material_data.get("guid", "")) == 0 \
return MaterialOutputModel(guid = material_data["guid"], else i18n_catalog.i18nc("@label:material", "Unknown")
type = material_data.get("type", ""), return MaterialOutputModel(guid = material_data.get("guid", ""),
color = material_data.get("color", ""), type = material_data.get("type", ""),
brand = material_data.get("brand", ""), color = material_data.get("color", ""),
name = material_data.get("name", material_name) brand = material_data.get("brand", ""),
) name = material_data.get("name", material_name)
)
# Sort the material groups by "is_read_only = True" first, and then the name alphabetically. # Sort the material groups by "is_read_only = True" first, and then the name alphabetically.
read_only_material_group_list = list(filter(lambda x: x.is_read_only, material_group_list)) read_only_material_group_list = list(filter(lambda x: x.is_read_only, material_group_list))
@ -643,9 +650,10 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
color = material_data["color"] color = material_data["color"]
brand = material_data["brand"] brand = material_data["brand"]
material_type = material_data["material"] material_type = material_data["material"]
name = "Empty" if material_data["material"] == "empty" else "Unknown" name = i18n_catalog.i18nc("@label:material", "Empty") if material_data["material"] == "empty" \
return MaterialOutputModel(guid=material_data["guid"], type=material_type, else i18n_catalog.i18nc("@label:material", "Unknown")
brand=brand, color=color, name=name) return MaterialOutputModel(guid = material_data["guid"], type = material_type,
brand = brand, color = color, name = name)
def _updatePrinter(self, printer: PrinterOutputModel, data: Dict[str, Any]) -> None: def _updatePrinter(self, printer: PrinterOutputModel, data: Dict[str, Any]) -> None:
# For some unknown reason the cluster wants UUID for everything, except for sending a job directly to a printer. # For some unknown reason the cluster wants UUID for everything, except for sending a job directly to a printer.

View file

@ -0,0 +1,46 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.10
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3
import UM 1.2 as UM
import Cura 1.0 as Cura
Component
{
Item
{
Rectangle
{
anchors.right: parent.right
width: parent.width * 0.3
anchors.top: parent.top
anchors.bottom: parent.bottom
Cura.PrintMonitor
{
anchors.fill: parent
}
Rectangle
{
id: footerSeparator
width: parent.width
height: UM.Theme.getSize("wide_lining").height
color: UM.Theme.getColor("wide_lining")
anchors.bottom: monitorButton.top
anchors.bottomMargin: UM.Theme.getSize("thick_margin").height
}
// MonitorButton is actually the bottom footer panel.
Cura.MonitorButton
{
id: monitorButton
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
}
}
}
}

View file

@ -1,5 +1,6 @@
# Copyright (c) 2018 Ultimaker B.V. # Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
import os
from UM.Logger import Logger from UM.Logger import Logger
from UM.i18n import i18nCatalog from UM.i18n import i18nCatalog
@ -64,7 +65,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
self._accepts_commands = True self._accepts_commands = True
self._paused = False self._paused = False
self._printer_busy = False # when printer is preheating and waiting (M190/M109), or when waiting for action on the printer self._printer_busy = False # When printer is preheating and waiting (M190/M109), or when waiting for action on the printer
self.setConnectionText(catalog.i18nc("@info:status", "Connected via USB")) self.setConnectionText(catalog.i18nc("@info:status", "Connected via USB"))
@ -77,6 +78,8 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
self._firmware_name_requested = False self._firmware_name_requested = False
self._firmware_updater = AvrFirmwareUpdater(self) self._firmware_updater = AvrFirmwareUpdater(self)
self._monitor_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "MonitorItem.qml")
CuraApplication.getInstance().getOnExitCallbackManager().addCallback(self._checkActivePrintingUponAppExit) CuraApplication.getInstance().getOnExitCallbackManager().addCallback(self._checkActivePrintingUponAppExit)
# This is a callback function that checks if there is any printing in progress via USB when the application tries # This is a callback function that checks if there is any printing in progress via USB when the application tries

View file

@ -19,7 +19,7 @@ Cura.MachineAction
property bool heatupBedStarted: false property bool heatupBedStarted: false
property bool printerConnected: Cura.MachineManager.printerConnected property bool printerConnected: Cura.MachineManager.printerConnected
UM.I18nCatalog { id: catalog; name:"cura"} UM.I18nCatalog { id: catalog; name: "cura"}
Label Label
{ {
id: pageTitle id: pageTitle

View file

@ -36,7 +36,7 @@ UM.Dialog
width: parent.width width: parent.width
anchors.bottomMargin: UM.Theme.getSize("default_margin").height anchors.bottomMargin: UM.Theme.getSize("default_margin").height
UM.I18nCatalog { id: catalog; name:"cura" } UM.I18nCatalog { id: catalog; name: "cura" }
Button Button
{ {

View file

@ -356,6 +356,23 @@
} }
} }
}, },
"PreviewStage": {
"package_info": {
"package_id": "PreviewStage",
"package_type": "plugin",
"display_name": "Preview Stage",
"description": "Provides a preview stage in Cura.",
"package_version": "1.0.0",
"sdk_version": 5,
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
"display_name": "Ultimaker B.V.",
"email": "plugins@ultimaker.com",
"website": "https://ultimaker.com"
}
}
},
"RemovableDriveOutputDevice": { "RemovableDriveOutputDevice": {
"package_info": { "package_info": {
"package_id": "RemovableDriveOutputDevice", "package_id": "RemovableDriveOutputDevice",

View file

@ -1,170 +0,0 @@
// Copyright (c) 2015 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Window 2.1
import UM 1.1 as UM
UM.Dialog
{
id: base
//: About dialog title
title: catalog.i18nc("@title:window","About Cura")
minimumWidth: 500 * screenScaleFactor
minimumHeight: 650 * screenScaleFactor
width: minimumWidth
height: minimumHeight
Rectangle
{
width: parent.width + 2 * margin // margin from Dialog.qml
height: version.y + version.height + margin
anchors.top: parent.top
anchors.topMargin: - margin
anchors.horizontalCenter: parent.horizontalCenter
color: UM.Theme.getColor("viewport_background")
}
Image
{
id: logo
width: (base.minimumWidth * 0.85) | 0
height: (width * (1/4.25)) | 0
source: UM.Theme.getImage("logo")
sourceSize.width: width
sourceSize.height: height
anchors.top: parent.top
anchors.topMargin: ((base.minimumWidth - width) / 2) | 0
anchors.horizontalCenter: parent.horizontalCenter
UM.I18nCatalog{id: catalog; name:"cura"}
}
Label
{
id: version
text: catalog.i18nc("@label","version: %1").arg(UM.Application.version)
font: UM.Theme.getFont("large")
color: UM.Theme.getColor("text")
anchors.right : logo.right
anchors.top: logo.bottom
anchors.topMargin: (UM.Theme.getSize("default_margin").height / 2) | 0
}
Label
{
id: description
width: parent.width
//: About dialog application description
text: catalog.i18nc("@label","End-to-end solution for fused filament 3D printing.")
font: UM.Theme.getFont("system")
wrapMode: Text.WordWrap
anchors.top: version.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
}
Label
{
id: creditsNotes
width: parent.width
//: About dialog application author note
text: catalog.i18nc("@info:credit","Cura is developed by Ultimaker B.V. in cooperation with the community.\nCura proudly uses the following open source projects:")
font: UM.Theme.getFont("system")
wrapMode: Text.WordWrap
anchors.top: description.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
}
ScrollView
{
id: credits
anchors.top: creditsNotes.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
width: parent.width
height: base.height - y - (2 * UM.Theme.getSize("default_margin").height + closeButton.height)
ListView
{
id: projectsList
width: parent.width
delegate: Row
{
Label
{
text: "<a href='%1' title='%2'>%2</a>".arg(model.url).arg(model.name)
width: (projectsList.width * 0.25) | 0
elide: Text.ElideRight
onLinkActivated: Qt.openUrlExternally(link)
}
Label
{
text: model.description
elide: Text.ElideRight
width: (projectsList.width * 0.6) | 0
}
Label
{
text: model.license
elide: Text.ElideRight
width: (projectsList.width * 0.15) | 0
}
}
model: ListModel
{
id: projectsModel
}
Component.onCompleted:
{
projectsModel.append({ name:"Cura", description: catalog.i18nc("@label", "Graphical user interface"), license: "LGPLv3", url: "https://github.com/Ultimaker/Cura" });
projectsModel.append({ name:"Uranium", description: catalog.i18nc("@label", "Application framework"), license: "LGPLv3", url: "https://github.com/Ultimaker/Uranium" });
projectsModel.append({ name:"CuraEngine", description: catalog.i18nc("@label", "G-code generator"), license: "AGPLv3", url: "https://github.com/Ultimaker/CuraEngine" });
projectsModel.append({ name:"libArcus", description: catalog.i18nc("@label", "Interprocess communication library"), license: "LGPLv3", url: "https://github.com/Ultimaker/libArcus" });
projectsModel.append({ name:"Python", description: catalog.i18nc("@label", "Programming language"), license: "Python", url: "http://python.org/" });
projectsModel.append({ name:"Qt5", description: catalog.i18nc("@label", "GUI framework"), license: "LGPLv3", url: "https://www.qt.io/" });
projectsModel.append({ name:"PyQt", description: catalog.i18nc("@label", "GUI framework bindings"), license: "GPL", url: "https://riverbankcomputing.com/software/pyqt" });
projectsModel.append({ name:"SIP", description: catalog.i18nc("@label", "C/C++ Binding library"), license: "GPL", url: "https://riverbankcomputing.com/software/sip" });
projectsModel.append({ name:"Protobuf", description: catalog.i18nc("@label", "Data interchange format"), license: "BSD", url: "https://developers.google.com/protocol-buffers" });
projectsModel.append({ name:"SciPy", description: catalog.i18nc("@label", "Support library for scientific computing"), license: "BSD-new", url: "https://www.scipy.org/" });
projectsModel.append({ name:"NumPy", description: catalog.i18nc("@label", "Support library for faster math"), license: "BSD", url: "http://www.numpy.org/" });
projectsModel.append({ name:"NumPy-STL", description: catalog.i18nc("@label", "Support library for handling STL files"), license: "BSD", url: "https://github.com/WoLpH/numpy-stl" });
projectsModel.append({ name:"Shapely", description: catalog.i18nc("@label", "Support library for handling planar objects"), license: "BSD", url: "https://github.com/Toblerity/Shapely" });
projectsModel.append({ name:"Trimesh", description: catalog.i18nc("@label", "Support library for handling triangular meshes"), license: "MIT", url: "https://trimsh.org" });
projectsModel.append({ name:"NetworkX", description: catalog.i18nc("@label", "Support library for analysis of complex networks"), license: "3-clause BSD", url: "https://networkx.github.io/" });
projectsModel.append({ name:"libSavitar", description: catalog.i18nc("@label", "Support library for handling 3MF files"), license: "LGPLv3", url: "https://github.com/ultimaker/libsavitar" });
projectsModel.append({ name:"libCharon", description: catalog.i18nc("@label", "Support library for file metadata and streaming"), license: "LGPLv3", url: "https://github.com/ultimaker/libcharon" });
projectsModel.append({ name:"PySerial", description: catalog.i18nc("@label", "Serial communication library"), license: "Python", url: "http://pyserial.sourceforge.net/" });
projectsModel.append({ name:"python-zeroconf", description: catalog.i18nc("@label", "ZeroConf discovery library"), license: "LGPL", url: "https://github.com/jstasiak/python-zeroconf" });
projectsModel.append({ name:"Clipper", description: catalog.i18nc("@label", "Polygon clipping library"), license: "Boost", url: "http://www.angusj.com/delphi/clipper.php" });
projectsModel.append({ name:"Requests", description: catalog.i18nc("@Label", "Python HTTP library"), license: "GPL", url: "http://docs.python-requests.org" });
projectsModel.append({ name:"Noto Sans", description: catalog.i18nc("@label", "Font"), license: "Apache 2.0", url: "https://www.google.com/get/noto/" });
projectsModel.append({ name:"Font-Awesome-SVG-PNG", description: catalog.i18nc("@label", "SVG icons"), license: "SIL OFL 1.1", url: "https://github.com/encharm/Font-Awesome-SVG-PNG" });
projectsModel.append({ name:"AppImageKit", description: catalog.i18nc("@label", "Linux cross-distribution application deployment"), license: "MIT", url: "https://github.com/AppImage/AppImageKit" });
}
}
}
rightButtons: Button
{
//: Close about dialog button
id: closeButton
text: catalog.i18nc("@action:button","Close");
onClicked: base.visible = false;
}
}

View file

@ -0,0 +1,69 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.7
import QtQuick.Controls 2.1
import UM 1.4 as UM
import Cura 1.1 as Cura
Column
{
property var profile: null
property var loggedIn: false
property var profileImage: ""
padding: UM.Theme.getSize("wide_margin").height
spacing: UM.Theme.getSize("wide_margin").height
AvatarImage
{
id: avatar
width: UM.Theme.getSize("avatar_image").width
height: UM.Theme.getSize("avatar_image").height
anchors.horizontalCenter: parent.horizontalCenter
source:
{
if(loggedIn)
{
if(profileImage)
{
return profileImage
}
return UM.Theme.getImage("avatar_no_user")
}
return UM.Theme.getImage("avatar_no_user")
}
outlineColor: loggedIn ? UM.Theme.getColor("account_widget_outline_active") : UM.Theme.getColor("lining")
}
Label
{
id: information
anchors.horizontalCenter: parent.horizontalCenter
horizontalAlignment: Text.AlignHCenter
renderType: Text.NativeRendering
text: loggedIn ? profile["username"] : catalog.i18nc("@label", "Please log in or create an account to\nenjoy all features of Ultimaker Cura.")
font: loggedIn ? UM.Theme.getFont("large") : UM.Theme.getFont("default")
color: UM.Theme.getColor("text")
}
Loader
{
id: accountOperations
anchors.horizontalCenter: parent.horizontalCenter
sourceComponent: loggedIn ? userOperations : generalOperations
}
Component
{
id: userOperations
UserOperations { }
}
Component
{
id: generalOperations
GeneralOperations { }
}
}

View file

@ -0,0 +1,76 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.7
import QtQuick.Controls 2.1
import UM 1.4 as UM
import Cura 1.1 as Cura
Button
{
id: accountWidget
property var profile: Cura.API.account.userProfile
property var loggedIn: Cura.API.account.isLoggedIn
implicitHeight: UM.Theme.getSize("main_window_header").height
implicitWidth: UM.Theme.getSize("main_window_header").height
background: AvatarImage
{
id: avatar
width: Math.round(0.8 * accountWidget.width)
height: Math.round(0.8 * accountWidget.height)
anchors.verticalCenter: accountWidget.verticalCenter
anchors.horizontalCenter: accountWidget.horizontalCenter
source:
{
if(loggedIn)
{
if(profile["profile_image_url"])
{
return profile["profile_image_url"]
}
return UM.Theme.getImage("avatar_no_user")
}
return UM.Theme.getImage("avatar_no_user")
}
outlineColor: loggedIn ? UM.Theme.getColor("account_widget_outline_active") : UM.Theme.getColor("lining")
}
onClicked: popup.opened ? popup.close() : popup.open()
Popup
{
id: popup
y: parent.height + UM.Theme.getSize("default_arrow").height
x: parent.width - width
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
opacity: opened ? 1 : 0
Behavior on opacity { NumberAnimation { duration: 100 } }
contentItem: AccountDetails
{
id: panel
profile: Cura.API.account.userProfile
loggedIn: Cura.API.account.isLoggedIn
profileImage: Cura.API.account.profileImageUrl
}
background: UM.PointingRectangle
{
color: UM.Theme.getColor("tool_panel_background")
borderColor: UM.Theme.getColor("lining")
borderWidth: UM.Theme.getSize("default_lining").width
target: Qt.point(width - (accountWidget.width / 2), -10)
arrowSize: UM.Theme.getSize("default_arrow").width
}
}
}

View file

@ -0,0 +1,56 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.7
import QtQuick.Controls 2.1
import QtGraphicalEffects 1.0
import UM 1.4 as UM
Item
{
// This item shows the provided image while applying a round mask to it.
// It also shows a round border around it. The color is defined by the outlineColor property.
id: avatar
property alias source: profileImage.source
property alias outlineColor: profileImageOutline.color
Image
{
id: profileImage
anchors.fill: parent
source: UM.Theme.getImage("avatar_default")
fillMode: Image.PreserveAspectCrop
visible: false
mipmap: true
}
Rectangle
{
id: profileImageMask
anchors.fill: parent
radius: width
}
OpacityMask
{
anchors.fill: parent
source: profileImage
maskSource: profileImageMask
cached: true
}
UM.RecolorImage
{
id: profileImageOutline
anchors.centerIn: parent
// Make it a bit bigger than it has to, otherwise it sometimes shows a white border.
width: parent.width + 2
height: parent.height + 2
source: UM.Theme.getIcon("circle_outline")
sourceSize: Qt.size(parent.width, parent.height)
color: UM.Theme.getColor("account_widget_ouline_active")
}
}

View file

@ -0,0 +1,31 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.2
import QtQuick.Controls 1.1
import UM 1.4 as UM
import Cura 1.1 as Cura
Row
{
spacing: UM.Theme.getSize("default_margin").width
Cura.SecondaryButton
{
width: UM.Theme.getSize("account_button").width
height: UM.Theme.getSize("account_button").height
text: catalog.i18nc("@button", "Create account")
onClicked: Qt.openUrlExternally("https://account.ultimaker.com/app/create")
fixedWidthMode: true
}
Cura.PrimaryButton
{
width: UM.Theme.getSize("account_button").width
height: UM.Theme.getSize("account_button").height
text: catalog.i18nc("@button", "Login")
onClicked: Cura.API.account.login()
fixedWidthMode: true
}
}

View file

@ -0,0 +1,31 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.2
import QtQuick.Controls 1.1
import UM 1.4 as UM
import Cura 1.1 as Cura
Row
{
spacing: UM.Theme.getSize("default_margin").width
Cura.SecondaryButton
{
width: UM.Theme.getSize("account_button").width
height: UM.Theme.getSize("account_button").height
text: catalog.i18nc("@button", "Manage account")
onClicked: Qt.openUrlExternally("https://account.ultimaker.com")
fixedWidthMode: true
}
Cura.PrimaryButton
{
width: UM.Theme.getSize("account_button").width
height: UM.Theme.getSize("account_button").height
text: catalog.i18nc("@button", "Logout")
onClicked: Cura.API.account.logout()
fixedWidthMode: true
}
}

View file

@ -0,0 +1,119 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.7
import QtQuick.Controls 2.1
import QtGraphicalEffects 1.0 // For the dropshadow
import UM 1.1 as UM
import Cura 1.0 as Cura
Button
{
id: button
property bool isIconOnRightSide: false
property alias iconSource: buttonIconLeft.source
property alias textFont: buttonText.font
property alias cornerRadius: backgroundRect.radius
property alias tooltip: tooltip.text
property alias cornerSide: backgroundRect.cornerSide
property color color: UM.Theme.getColor("primary")
property color hoverColor: UM.Theme.getColor("primary_hover")
property color disabledColor: color
property color textColor: UM.Theme.getColor("button_text")
property color textHoverColor: textColor
property color textDisabledColor: textColor
property color outlineColor: color
property color outlineHoverColor: hoverColor
property color outlineDisabledColor: outlineColor
property alias shadowColor: shadow.color
property alias shadowEnabled: shadow.visible
// This property is used to indicate whether the button has a fixed width or the width would depend on the contents
// Be careful when using fixedWidthMode, the translated texts can be too long that they won't fit. In any case,
// we elide the text to the right so the text will be cut off with the three dots at the end.
property var fixedWidthMode: false
leftPadding: UM.Theme.getSize("default_margin").width
rightPadding: UM.Theme.getSize("default_margin").width
height: UM.Theme.getSize("action_button").height
hoverEnabled: true
contentItem: Row
{
//Left side icon. Only displayed if !isIconOnRightSide.
UM.RecolorImage
{
id: buttonIconLeft
source: ""
height: buttonText.height
width: visible ? height : 0
sourceSize.width: width
sourceSize.height: height
color: button.hovered ? button.textHoverColor : button.textColor
visible: source != "" && !button.isIconOnRightSide
anchors.verticalCenter: parent.verticalCenter
}
Label
{
id: buttonText
text: button.text
color: button.enabled ? (button.hovered ? button.textHoverColor : button.textColor): button.textDisabledColor
font: UM.Theme.getFont("action_button")
visible: text != ""
renderType: Text.NativeRendering
anchors.verticalCenter: parent.verticalCenter
width: fixedWidthMode ? button.width - button.leftPadding - button.rightPadding : undefined
horizontalAlignment: Text.AlignHCenter
elide: Text.ElideRight
}
//Right side icon. Only displayed if isIconOnRightSide.
UM.RecolorImage
{
id: buttonIconRight
source: buttonIconLeft.source
height: buttonText.height
width: visible ? height : 0
sourceSize.width: width
sourceSize.height: height
color: buttonIconLeft.color
visible: source != "" && button.isIconOnRightSide
anchors.verticalCenter: buttonIconLeft.verticalCenter
}
}
background: Cura.RoundedRectangle
{
id: backgroundRect
cornerSide: Cura.RoundedRectangle.Direction.All
color: button.enabled ? (button.hovered ? button.hoverColor : button.color) : button.disabledColor
radius: UM.Theme.getSize("action_button_radius").width
border.width: UM.Theme.getSize("default_lining").width
border.color: button.enabled ? (button.hovered ? button.outlineHoverColor : button.outlineColor) : button.outlineDisabledColor
}
DropShadow
{
id: shadow
// Don't blur the shadow
radius: 0
anchors.fill: backgroundRect
source: backgroundRect
verticalOffset: 2
visible: false
// Should always be drawn behind the background.
z: backgroundRect.z - 1
}
ToolTip
{
id: tooltip
text: ""
delay: 500
visible: text != "" && button.hovered
}
}

View file

@ -0,0 +1,55 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.7
import QtQuick.Controls 2.1
import QtQuick.Layouts 1.3
import UM 1.2 as UM
import Cura 1.0 as Cura
// This element hold all the elements needed for the user to trigger the slicing process, and later
// to get information about the printing times, material consumption and the output process (such as
// saving to a file, printing over network, ...
Rectangle
{
id: actionPanelWidget
width: UM.Theme.getSize("action_panel_widget").width
height: childrenRect.height + 2 * UM.Theme.getSize("thick_margin").height
color: UM.Theme.getColor("main_background")
border.width: UM.Theme.getSize("default_lining").width
border.color: UM.Theme.getColor("lining")
radius: UM.Theme.getSize("default_radius").width
property bool outputAvailable: UM.Backend.state == UM.Backend.Done || UM.Backend.state == UM.Backend.Disabled
Loader
{
id: loader
anchors
{
top: parent.top
topMargin: UM.Theme.getSize("thick_margin").height
left: parent.left
leftMargin: UM.Theme.getSize("thick_margin").width
right: parent.right
rightMargin: UM.Theme.getSize("thick_margin").width
}
sourceComponent: outputAvailable ? outputProcessWidget : sliceProcessWidget
}
Component
{
id: sliceProcessWidget
SliceProcessWidget { }
}
Component
{
id: outputProcessWidget
OutputProcessWidget { }
}
}

View file

@ -0,0 +1,107 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.7
import QtQuick.Controls 2.1
import QtQuick.Layouts 1.3
import UM 1.1 as UM
import Cura 1.0 as Cura
Item
{
id: widget
Cura.PrimaryButton
{
id: saveToButton
height: parent.height
fixedWidthMode: true
cornerSide: deviceSelectionMenu.visible ? Cura.RoundedRectangle.Direction.Left : Cura.RoundedRectangle.Direction.All
anchors
{
top: parent.top
left: parent.left
right: deviceSelectionMenu.visible ? deviceSelectionMenu.left : parent.right
}
tooltip: UM.OutputDeviceManager.activeDeviceDescription
text: UM.OutputDeviceManager.activeDeviceShortDescription
onClicked:
{
forceActiveFocus();
UM.OutputDeviceManager.requestWriteToDevice(UM.OutputDeviceManager.activeDevice, PrintInformation.jobName,
{ "filter_by_machine": true, "preferred_mimetypes": Cura.MachineManager.activeMachine.preferred_output_file_formats });
}
}
Cura.ActionButton
{
id: deviceSelectionMenu
height: parent.height
shadowEnabled: true
shadowColor: UM.Theme.getColor("primary_shadow")
cornerSide: Cura.RoundedRectangle.Direction.Right
anchors
{
top: parent.top
right: parent.right
}
leftPadding: UM.Theme.getSize("narrow_margin").width //Need more space than usual here for wide text.
rightPadding: UM.Theme.getSize("narrow_margin").width
tooltip: popup.opened ? "" : catalog.i18nc("@info:tooltip", "Select the active output device")
iconSource: popup.opened ? UM.Theme.getIcon("arrow_top") : UM.Theme.getIcon("arrow_bottom")
color: UM.Theme.getColor("action_panel_secondary")
visible: (devicesModel.deviceCount > 1)
onClicked: popup.opened ? popup.close() : popup.open()
Popup
{
id: popup
padding: 0
y: -height
x: parent.width - width
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
contentItem: ColumnLayout
{
Repeater
{
model: devicesModel
delegate: Cura.ActionButton
{
text: model.description
color: "transparent"
cornerRadius: 0
hoverColor: UM.Theme.getColor("primary")
Layout.fillWidth: true
onClicked:
{
UM.OutputDeviceManager.setActiveDevice(model.id)
popup.close()
}
}
}
}
background: Rectangle
{
opacity: visible ? 1 : 0
Behavior on opacity { NumberAnimation { duration: 100 } }
color: UM.Theme.getColor("action_panel_secondary")
}
}
}
UM.OutputDevicesModel { id: devicesModel }
}

View file

@ -0,0 +1,142 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.7
import QtQuick.Controls 2.1
import QtQuick.Layouts 1.3
import UM 1.1 as UM
import Cura 1.0 as Cura
// This element contains all the elements the user needs to visualize the data
// that is gather after the slicing process, such as printint time, material usage, ...
// There are also two buttons: one to previsualize the output layers, and the other to
// select what to do with it (such as print over network, save to file, ...)
Column
{
id: widget
spacing: UM.Theme.getSize("thin_margin").height
property bool preSlicedData: PrintInformation.preSliced
UM.I18nCatalog
{
id: catalog
name: "cura"
}
Item
{
id: information
width: parent.width
height: childrenRect.height
Column
{
id: timeAndCostsInformation
spacing: UM.Theme.getSize("thin_margin").height
anchors
{
left: parent.left
right: printInformationPanel.left
rightMargin: UM.Theme.getSize("thin_margin").height
}
Cura.IconWithText
{
id: estimatedTime
width: parent.width
text: preSlicedData ? catalog.i18nc("@label", "No time estimation available") : PrintInformation.currentPrintTime.getDisplayString(UM.DurationFormat.Long)
source: UM.Theme.getIcon("clock")
font: UM.Theme.getFont("default_bold")
}
Cura.IconWithText
{
id: estimatedCosts
width: parent.width
property var printMaterialLengths: PrintInformation.materialLengths
property var printMaterialWeights: PrintInformation.materialWeights
text:
{
if (preSlicedData)
{
return catalog.i18nc("@label", "No cost estimation available")
}
var totalLengths = 0
var totalWeights = 0
if (printMaterialLengths)
{
for(var index = 0; index < printMaterialLengths.length; index++)
{
if(printMaterialLengths[index] > 0)
{
totalLengths += printMaterialLengths[index]
totalWeights += Math.round(printMaterialWeights[index])
}
}
}
return totalWeights + "g · " + totalLengths.toFixed(2) + "m"
}
source: UM.Theme.getIcon("spool")
}
}
PrintInformationWidget
{
id: printInformationPanel
visible: !preSlicedData
anchors
{
right: parent.right
verticalCenter: timeAndCostsInformation.verticalCenter
}
}
}
Item
{
id: buttonRow
anchors.right: parent.right
anchors.left: parent.left
height: UM.Theme.getSize("action_button").height
Cura.SecondaryButton
{
id: previewStageShortcut
anchors
{
left: parent.left
right: outputDevicesButton.left
rightMargin: UM.Theme.getSize("default_margin").width
}
height: UM.Theme.getSize("action_button").height
leftPadding: UM.Theme.getSize("default_margin").width
rightPadding: UM.Theme.getSize("default_margin").width
text: catalog.i18nc("@button", "Preview")
onClicked: UM.Controller.setActiveStage("PreviewStage")
visible: UM.Controller.activeStage != null && UM.Controller.activeStage.stageId != "PreviewStage"
shadowEnabled: true
shadowColor: UM.Theme.getColor("action_button_disabled_shadow")
}
Cura.OutputDevicesActionButton
{
id: outputDevicesButton
anchors.right: parent.right
width: previewStageShortcut.visible ? UM.Theme.getSize("action_button").width : parent.width
height: UM.Theme.getSize("action_button").height
}
}
}

View file

@ -0,0 +1,58 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.7
import QtQuick.Controls 2.1
import UM 1.1 as UM
import Cura 1.0 as Cura
UM.RecolorImage
{
id: widget
source: UM.Theme.getIcon("info")
width: UM.Theme.getSize("section_icon").width
height: UM.Theme.getSize("section_icon").height
color: popup.opened ? UM.Theme.getColor("primary") : UM.Theme.getColor("text_medium")
MouseArea
{
anchors.fill: parent
hoverEnabled: true
onEntered: popup.open()
onExited: popup.close()
}
Popup
{
id: popup
y: -(height + UM.Theme.getSize("default_arrow").height + UM.Theme.getSize("thin_margin").height)
x: parent.width - width + UM.Theme.getSize("thin_margin").width
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
opacity: opened ? 1 : 0
Behavior on opacity { NumberAnimation { duration: 100 } }
contentItem: PrintJobInformation
{
id: printJobInformation
width: UM.Theme.getSize("action_panel_information_widget").width
}
background: UM.PointingRectangle
{
color: UM.Theme.getColor("tool_panel_background")
borderColor: UM.Theme.getColor("lining")
borderWidth: UM.Theme.getSize("default_lining").width
target: Qt.point(width - (widget.width / 2) - UM.Theme.getSize("thin_margin").width,
height + UM.Theme.getSize("default_arrow").height - UM.Theme.getSize("thin_margin").height)
arrowSize: UM.Theme.getSize("default_arrow").width
}
}
}

View file

@ -0,0 +1,159 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.7
import QtQuick.Controls 2.1
import UM 1.1 as UM
import Cura 1.0 as Cura
Column
{
id: base
spacing: UM.Theme.getSize("default_margin").width
UM.I18nCatalog
{
id: catalog
name: "cura"
}
Column
{
id: timeSpecification
width: parent.width
topPadding: UM.Theme.getSize("default_margin").height
leftPadding: UM.Theme.getSize("default_margin").width
rightPadding: UM.Theme.getSize("default_margin").width
Label
{
text: catalog.i18nc("@label", "Time specification").toUpperCase()
color: UM.Theme.getColor("primary")
font: UM.Theme.getFont("default_bold")
renderType: Text.NativeRendering
}
Label
{
property var printDuration: PrintInformation.currentPrintTime
text:
{
// All the time information for the different features is achieved
var printTime = PrintInformation.getFeaturePrintTimes()
var totalSeconds = parseInt(printDuration.getDisplayString(UM.DurationFormat.Seconds))
// A message is created and displayed when the user hover the time label
var text = "<table width=\"100%\">"
for(var feature in printTime)
{
if(!printTime[feature].isTotalDurationZero)
{
text += "<tr><td>" + feature + ":</td>" +
"<td align=\"right\" valign=\"bottom\">&nbsp;&nbsp;%1</td>".arg(printTime[feature].getDisplayString(UM.DurationFormat.ISO8601).slice(0,-3)) +
"<td align=\"right\" valign=\"bottom\">&nbsp;&nbsp;%1%</td>".arg(Math.round(100 * parseInt(printTime[feature].getDisplayString(UM.DurationFormat.Seconds)) / totalSeconds)) +
"</tr>"
}
}
text += "</table>"
return text
}
width: parent.width - 2 * UM.Theme.getSize("default_margin").width
color: UM.Theme.getColor("text")
font: UM.Theme.getFont("default")
renderType: Text.NativeRendering
textFormat: Text.RichText
}
}
Column
{
id: materialSpecification
width: parent.width
bottomPadding: UM.Theme.getSize("default_margin").height
leftPadding: UM.Theme.getSize("default_margin").width
rightPadding: UM.Theme.getSize("default_margin").width
Label
{
text: catalog.i18nc("@label", "Material specification").toUpperCase()
color: UM.Theme.getColor("primary")
font: UM.Theme.getFont("default_bold")
renderType: Text.NativeRendering
}
Label
{
property var printMaterialLengths: PrintInformation.materialLengths
property var printMaterialWeights: PrintInformation.materialWeights
property var printMaterialCosts: PrintInformation.materialCosts
property var printMaterialNames: PrintInformation.materialNames
function formatRow(items)
{
var rowHTML = "<tr>"
for(var item = 0; item < items.length; item++)
{
if (item == 0)
{
rowHTML += "<td valign=\"bottom\">%1</td>".arg(items[item])
}
else
{
rowHTML += "<td align=\"right\" valign=\"bottom\">&nbsp;&nbsp;%1</td>".arg(items[item])
}
}
rowHTML += "</tr>"
return rowHTML
}
text:
{
var lengths = []
var weights = []
var costs = []
var names = []
if(printMaterialLengths)
{
for(var index = 0; index < printMaterialLengths.length; index++)
{
if(printMaterialLengths[index] > 0)
{
names.push(printMaterialNames[index])
lengths.push(printMaterialLengths[index].toFixed(2))
weights.push(String(Math.round(printMaterialWeights[index])))
var cost = printMaterialCosts[index] == undefined ? 0 : printMaterialCosts[index].toFixed(2)
costs.push(cost)
}
}
}
if(lengths.length == 0)
{
lengths = ["0.00"]
weights = ["0"]
costs = ["0.00"]
}
var text = "<table width=\"100%\">"
for(var index = 0; index < lengths.length; index++)
{
text += formatRow([
"%1:".arg(names[index]),
catalog.i18nc("@label m for meter", "%1m").arg(lengths[index]),
catalog.i18nc("@label g for grams", "%1g").arg(weights[index]),
"%1&nbsp;%2".arg(UM.Preferences.getValue("cura/currency")).arg(costs[index]),
])
}
text += "</table>"
return text
}
width: parent.width - 2 * UM.Theme.getSize("default_margin").width
color: UM.Theme.getColor("text")
font: UM.Theme.getFont("default")
renderType: Text.NativeRendering
textFormat: Text.RichText
}
}
}

View file

@ -0,0 +1,157 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.7
import QtQuick.Controls 2.1
import QtQuick.Layouts 1.3
import QtQuick.Controls 1.4 as Controls1
import UM 1.1 as UM
import Cura 1.0 as Cura
// This element contains all the elements the user needs to create a printjob from the
// model(s) that is(are) on the buildplate. Mainly the button to start/stop the slicing
// process and a progress bar to see the progress of the process.
Column
{
id: widget
spacing: UM.Theme.getSize("thin_margin").height
UM.I18nCatalog
{
id: catalog
name: "cura"
}
property real progress: UM.Backend.progress
property int backendState: UM.Backend.state
function sliceOrStopSlicing()
{
if (widget.backendState == UM.Backend.NotStarted)
{
CuraApplication.backend.forceSlice()
}
else
{
CuraApplication.backend.stopSlicing()
}
}
Label
{
id: autoSlicingLabel
width: parent.width
visible: prepareButtons.autoSlice && (widget.backendState == UM.Backend.Processing || widget.backendState == UM.Backend.NotStarted)
text: catalog.i18nc("@label:PrintjobStatus", "Auto slicing...")
color: UM.Theme.getColor("text")
font: UM.Theme.getFont("default")
renderType: Text.NativeRendering
}
Cura.IconWithText
{
id: unableToSliceMessage
width: parent.width
visible: widget.backendState == UM.Backend.Error
text: catalog.i18nc("@label:PrintjobStatus", "Unable to Slice")
source: UM.Theme.getIcon("warning")
color: UM.Theme.getColor("warning")
}
// Progress bar, only visible when the backend is in the process of slice the printjob
ProgressBar
{
id: progressBar
width: parent.width
height: UM.Theme.getSize("progressbar").height
value: progress
indeterminate: widget.backendState == UM.Backend.NotStarted
visible: (widget.backendState == UM.Backend.Processing || (prepareButtons.autoSlice && widget.backendState == UM.Backend.NotStarted))
background: Rectangle
{
anchors.fill: parent
radius: UM.Theme.getSize("progressbar_radius").width
color: UM.Theme.getColor("progressbar_background")
}
contentItem: Item
{
anchors.fill: parent
Rectangle
{
width: progressBar.visualPosition * parent.width
height: parent.height
radius: UM.Theme.getSize("progressbar_radius").width
color: UM.Theme.getColor("progressbar_control")
}
}
}
Item
{
id: prepareButtons
// Get the current value from the preferences
property bool autoSlice: UM.Preferences.getValue("general/auto_slice")
// Disable the slice process when
width: parent.width
height: UM.Theme.getSize("action_button").height
visible: !autoSlice
Cura.PrimaryButton
{
id: sliceButton
fixedWidthMode: true
anchors.fill: parent
text: catalog.i18nc("@button", "Slice")
enabled: widget.backendState != UM.Backend.Error
visible: widget.backendState == UM.Backend.NotStarted || widget.backendState == UM.Backend.Error
onClicked: sliceOrStopSlicing()
}
Cura.SecondaryButton
{
id: cancelButton
fixedWidthMode: true
anchors.fill: parent
text: catalog.i18nc("@button", "Cancel")
enabled: sliceButton.enabled
visible: !sliceButton.visible
onClicked: sliceOrStopSlicing()
}
}
// React when the user changes the preference of having the auto slice enabled
Connections
{
target: UM.Preferences
onPreferenceChanged:
{
var autoSlice = UM.Preferences.getValue("general/auto_slice")
prepareButtons.autoSlice = autoSlice
if(autoSlice)
{
CuraApplication.backend.forceSlice()
}
}
}
// Shortcut for "slice/stop"
Controls1.Action
{
shortcut: "Ctrl+P"
onTriggered:
{
if (prepareButton.enabled)
{
sliceOrStopSlicing()
}
}
}
}

View file

@ -1,4 +1,4 @@
// Copyright (c) 2015 Ultimaker B.V. // Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher. // Cura is released under the terms of the LGPLv3 or higher.
pragma Singleton pragma Singleton
@ -23,8 +23,6 @@ Item
property alias viewLeftSideCamera: viewLeftSideCameraAction; property alias viewLeftSideCamera: viewLeftSideCameraAction;
property alias viewRightSideCamera: viewRightSideCameraAction; property alias viewRightSideCamera: viewRightSideCameraAction;
property alias expandSidebar: expandSidebarAction;
property alias deleteSelection: deleteSelectionAction; property alias deleteSelection: deleteSelectionAction;
property alias centerSelection: centerSelectionAction; property alias centerSelection: centerSelectionAction;
property alias multiplySelection: multiplySelectionAction; property alias multiplySelection: multiplySelectionAction;
@ -58,7 +56,6 @@ Item
property alias preferences: preferencesAction; property alias preferences: preferencesAction;
property alias showEngineLog: showEngineLogAction;
property alias showProfileFolder: showProfileFolderAction; property alias showProfileFolder: showProfileFolderAction;
property alias documentation: documentationAction; property alias documentation: documentationAction;
property alias reportBug: reportBugAction; property alias reportBug: reportBugAction;
@ -70,7 +67,7 @@ Item
property alias browsePackages: browsePackagesAction property alias browsePackages: browsePackagesAction
UM.I18nCatalog{id: catalog; name:"cura"} UM.I18nCatalog{id: catalog; name: "cura"}
Action Action
{ {
@ -398,14 +395,6 @@ Item
shortcut: StandardKey.New shortcut: StandardKey.New
} }
Action
{
id: showEngineLogAction;
text: catalog.i18nc("@action:inmenu menubar:help","Show Engine &Log...");
iconName: "view-list-text";
shortcut: StandardKey.WhatsThis;
}
Action Action
{ {
id: showProfileFolderAction; id: showProfileFolderAction;
@ -426,11 +415,4 @@ Item
text: catalog.i18nc("@action:menu", "&Marketplace") text: catalog.i18nc("@action:menu", "&Marketplace")
iconName: "plugins_browse" iconName: "plugins_browse"
} }
Action
{
id: expandSidebarAction;
text: catalog.i18nc("@action:inmenu menubar:view","Expand/Collapse Sidebar");
shortcut: "Ctrl+E";
}
} }

View file

@ -0,0 +1,10 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.0
QtObject
{
property real width: 0
property color color: "black"
}

View file

@ -1,4 +1,4 @@
// Copyright (c) 2017 Ultimaker B.V. // Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher. // Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.7 import QtQuick 2.7
@ -6,53 +6,42 @@ import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4 import QtQuick.Controls.Styles 1.4
import QtQuick.Layouts 1.1 import QtQuick.Layouts 1.1
import QtQuick.Dialogs 1.2 import QtQuick.Dialogs 1.2
import QtGraphicalEffects 1.0
import UM 1.3 as UM import UM 1.3 as UM
import Cura 1.1 as Cura import Cura 1.1 as Cura
import "Dialogs"
import "Menus" import "Menus"
import "MainWindow"
UM.MainWindow UM.MainWindow
{ {
id: base id: base
//: Cura application window title
title: catalog.i18nc("@title:window","Ultimaker Cura");
viewportRect: Qt.rect(0, 0, (base.width - sidebar.width) / base.width, 1.0)
property bool showPrintMonitor: false
// Cura application window title
title: catalog.i18nc("@title:window", "Ultimaker Cura")
backgroundColor: UM.Theme.getColor("viewport_background") backgroundColor: UM.Theme.getColor("viewport_background")
// This connection is here to support legacy printer output devices that use the showPrintMonitor signal on Application to switch to the monitor stage
// It should be phased out in newer plugin versions. UM.I18nCatalog
Connections
{ {
target: CuraApplication id: catalog
onShowPrintMonitor: { name: "cura"
if (show) {
UM.Controller.setActiveStage("MonitorStage")
} else {
UM.Controller.setActiveStage("PrepareStage")
}
}
} }
onWidthChanged: function showTooltip(item, position, text)
{ {
// If slidebar is collapsed then it should be invisible tooltip.text = text;
// otherwise after the main_window resize the sidebar will be fully re-drawn position = item.mapToItem(backgroundItem, position.x - UM.Theme.getSize("default_arrow").width, position.y);
if (sidebar.collapsed){ tooltip.show(position);
if (sidebar.visible == true){
sidebar.visible = false
sidebar.initialWidth = 0
}
}
else{
if (sidebar.visible == false){
sidebar.visible = true
sidebar.initialWidth = UM.Theme.getSize("sidebar").width
}
}
} }
function hideTooltip()
{
tooltip.hide();
}
Component.onCompleted: Component.onCompleted:
{ {
CuraApplication.setMinimumWindowSize(UM.Theme.getSize("window_minimum_size")) CuraApplication.setMinimumWindowSize(UM.Theme.getSize("window_minimum_size"))
@ -72,12 +61,12 @@ UM.MainWindow
Item Item
{ {
id: backgroundItem; id: backgroundItem
anchors.fill: parent; anchors.fill: parent
UM.I18nCatalog{id: catalog; name:"cura"}
signal hasMesh(string name) //this signal sends the filebase name so it can be used for the JobSpecs.qml signal hasMesh(string name) //this signal sends the filebase name so it can be used for the JobSpecs.qml
function getMeshName(path){ function getMeshName(path)
{
//takes the path the complete path of the meshname and returns only the filebase //takes the path the complete path of the meshname and returns only the filebase
var fileName = path.slice(path.lastIndexOf("/") + 1) var fileName = path.slice(path.lastIndexOf("/") + 1)
var fileBase = fileName.slice(0, fileName.indexOf(".")) var fileBase = fileName.slice(0, fileName.indexOf("."))
@ -85,253 +74,86 @@ UM.MainWindow
} }
//DeleteSelection on the keypress backspace event //DeleteSelection on the keypress backspace event
Keys.onPressed: { Keys.onPressed:
{
if (event.key == Qt.Key_Backspace) if (event.key == Qt.Key_Backspace)
{ {
Cura.Actions.deleteSelection.trigger() Cura.Actions.deleteSelection.trigger()
} }
} }
UM.ApplicationMenu ApplicationMenu
{ {
id: menu id: applicationMenu
window: base window: base
Menu
{
id: fileMenu
title: catalog.i18nc("@title:menu menubar:toplevel","&File");
MenuItem
{
id: newProjectMenu
action: Cura.Actions.newProject;
}
MenuItem
{
id: openMenu
action: Cura.Actions.open;
}
RecentFilesMenu { }
MenuItem
{
id: saveWorkspaceMenu
text: catalog.i18nc("@title:menu menubar:file","&Save...")
onTriggered:
{
var args = { "filter_by_machine": false, "file_type": "workspace", "preferred_mimetypes": "application/vnd.ms-package.3dmanufacturing-3dmodel+xml" };
if(UM.Preferences.getValue("cura/dialog_on_project_save"))
{
saveWorkspaceDialog.args = args;
saveWorkspaceDialog.open()
}
else
{
UM.OutputDeviceManager.requestWriteToDevice("local_file", PrintInformation.jobName, args)
}
}
}
MenuSeparator { }
MenuItem
{
id: saveAsMenu
text: catalog.i18nc("@title:menu menubar:file", "&Export...")
onTriggered:
{
var localDeviceId = "local_file";
UM.OutputDeviceManager.requestWriteToDevice(localDeviceId, PrintInformation.jobName, { "filter_by_machine": false, "preferred_mimetypes": "application/vnd.ms-package.3dmanufacturing-3dmodel+xml"});
}
}
MenuItem
{
id: exportSelectionMenu
text: catalog.i18nc("@action:inmenu menubar:file", "Export Selection...");
enabled: UM.Selection.hasSelection;
iconName: "document-save-as";
onTriggered: UM.OutputDeviceManager.requestWriteSelectionToDevice("local_file", PrintInformation.jobName, { "filter_by_machine": false, "preferred_mimetypes": "application/vnd.ms-package.3dmanufacturing-3dmodel+xml"});
}
MenuSeparator { }
MenuItem
{
id: reloadAllMenu
action: Cura.Actions.reloadAll;
}
MenuSeparator { }
MenuItem { action: Cura.Actions.quit; }
}
Menu
{
title: catalog.i18nc("@title:menu menubar:toplevel","&Edit");
MenuItem { action: Cura.Actions.undo; }
MenuItem { action: Cura.Actions.redo; }
MenuSeparator { }
MenuItem { action: Cura.Actions.selectAll; }
MenuItem { action: Cura.Actions.arrangeAll; }
MenuItem { action: Cura.Actions.deleteSelection; }
MenuItem { action: Cura.Actions.deleteAll; }
MenuItem { action: Cura.Actions.resetAllTranslation; }
MenuItem { action: Cura.Actions.resetAll; }
MenuSeparator { }
MenuItem { action: Cura.Actions.groupObjects;}
MenuItem { action: Cura.Actions.mergeObjects;}
MenuItem { action: Cura.Actions.unGroupObjects;}
}
ViewMenu { title: catalog.i18nc("@title:menu", "&View") }
Menu
{
id: settingsMenu
title: catalog.i18nc("@title:menu", "&Settings")
PrinterMenu { title: catalog.i18nc("@title:menu menubar:settings", "&Printer") }
Instantiator
{
model: Cura.ExtrudersModel { simpleNames: true }
Menu {
title: model.name
NozzleMenu { title: Cura.MachineManager.activeDefinitionVariantsName; visible: Cura.MachineManager.hasVariants; extruderIndex: index }
MaterialMenu { title: catalog.i18nc("@title:menu", "&Material"); visible: Cura.MachineManager.hasMaterials; extruderIndex: index }
MenuSeparator
{
visible: Cura.MachineManager.hasVariants || Cura.MachineManager.hasMaterials
}
MenuItem
{
text: catalog.i18nc("@action:inmenu", "Set as Active Extruder")
onTriggered: Cura.MachineManager.setExtruderIndex(model.index)
}
MenuItem
{
text: catalog.i18nc("@action:inmenu", "Enable Extruder")
onTriggered: Cura.MachineManager.setExtruderEnabled(model.index, true)
visible: !Cura.MachineManager.getExtruder(model.index).isEnabled
}
MenuItem
{
text: catalog.i18nc("@action:inmenu", "Disable Extruder")
onTriggered: Cura.MachineManager.setExtruderEnabled(model.index, false)
visible: Cura.MachineManager.getExtruder(model.index).isEnabled
enabled: Cura.MachineManager.numberExtrudersEnabled > 1
}
}
onObjectAdded: settingsMenu.insertItem(index, object)
onObjectRemoved: settingsMenu.removeItem(object)
}
// TODO Only show in dev mode. Remove check when feature ready
BuildplateMenu { title: catalog.i18nc("@title:menu", "&Build plate"); visible: CuraSDKVersion == "dev" ? Cura.MachineManager.hasVariantBuildplates : false }
ProfileMenu { title: catalog.i18nc("@title:settings", "&Profile"); }
MenuSeparator { }
MenuItem { action: Cura.Actions.configureSettingVisibility }
}
Menu
{
id: extension_menu
title: catalog.i18nc("@title:menu menubar:toplevel","E&xtensions");
Instantiator
{
id: extensions
model: UM.ExtensionModel { }
Menu
{
id: sub_menu
title: model.name;
visible: actions != null
enabled: actions != null
Instantiator
{
model: actions
MenuItem
{
text: model.text
onTriggered: extensions.model.subMenuTriggered(name, model.text)
}
onObjectAdded: sub_menu.insertItem(index, object)
onObjectRemoved: sub_menu.removeItem(object)
}
}
onObjectAdded: extension_menu.insertItem(index, object)
onObjectRemoved: extension_menu.removeItem(object)
}
}
Menu
{
id: plugin_menu
title: catalog.i18nc("@title:menu menubar:toplevel", "&Marketplace")
MenuItem { action: Cura.Actions.browsePackages }
}
Menu
{
id: preferencesMenu
title: catalog.i18nc("@title:menu menubar:toplevel","P&references");
MenuItem { action: Cura.Actions.preferences; }
}
Menu
{
id: helpMenu
title: catalog.i18nc("@title:menu menubar:toplevel","&Help");
MenuItem { action: Cura.Actions.showProfileFolder; }
MenuItem { action: Cura.Actions.documentation; }
MenuItem { action: Cura.Actions.reportBug; }
MenuSeparator { }
MenuItem { action: Cura.Actions.about; }
}
}
UM.SettingPropertyProvider
{
id: machineExtruderCount
containerStack: Cura.MachineManager.activeMachine
key: "machine_extruder_count"
watchedProperties: [ "value" ]
storeIndex: 0
} }
Item Item
{ {
id: contentItem; id: headerBackground
anchors
{
top: applicationMenu.bottom
left: parent.left
right: parent.right
}
height: stageMenu.source != "" ? Math.round(mainWindowHeader.height + stageMenu.height / 2) : mainWindowHeader.height
y: menu.height LinearGradient
width: parent.width; {
height: parent.height - menu.height; anchors.fill: parent
start: Qt.point(0, 0)
end: Qt.point(parent.width, 0)
gradient: Gradient
{
GradientStop
{
position: 0.0
color: UM.Theme.getColor("main_window_header_background")
}
GradientStop
{
position: 0.5
color: UM.Theme.getColor("main_window_header_background_gradient")
}
GradientStop
{
position: 1.0
color: UM.Theme.getColor("main_window_header_background")
}
}
}
}
Keys.forwardTo: menu MainWindowHeader
{
id: mainWindowHeader
anchors
{
left: parent.left
right: parent.right
top: applicationMenu.bottom
}
}
Item
{
id: contentItem
anchors
{
top: mainWindowHeader.bottom
bottom: parent.bottom
left: parent.left
right: parent.right
}
Keys.forwardTo: applicationMenu
DropArea DropArea
{ {
anchors.fill: parent; // The drop area is here to handle files being dropped onto Cura.
anchors.fill: parent
onDropped: onDropped:
{ {
if (drop.urls.length > 0) if (drop.urls.length > 0)
@ -359,156 +181,110 @@ UM.MainWindow
} }
} }
JobSpecs
{
id: jobSpecs
anchors
{
bottom: parent.bottom;
right: sidebar.left;
bottomMargin: UM.Theme.getSize("default_margin").height;
rightMargin: UM.Theme.getSize("default_margin").width;
}
}
Button
{
id: openFileButton;
text: catalog.i18nc("@action:button","Open File");
iconSource: UM.Theme.getIcon("load")
style: UM.Theme.styles.tool_button
tooltip: ""
anchors
{
top: topbar.bottom;
topMargin: UM.Theme.getSize("default_margin").height;
left: parent.left;
}
action: Cura.Actions.open;
}
Toolbar Toolbar
{ {
id: toolbar; // The toolbar is the left bar that is populated by all the tools (which are dynamicly populated by
// plugins)
id: toolbar
property int mouseX: base.mouseX property int mouseX: base.mouseX
property int mouseY: base.mouseY property int mouseY: base.mouseY
anchors { anchors
top: openFileButton.bottom; {
topMargin: UM.Theme.getSize("window_margin").height; verticalCenter: parent.verticalCenter
left: parent.left; left: parent.left
} }
visible: CuraApplication.platformActivity && !PrintInformation.preSliced
} }
ObjectsList ObjectsList
{ {
id: objectsList; id: objectsList
visible: UM.Preferences.getValue("cura/use_multi_build_plate"); visible: UM.Preferences.getValue("cura/use_multi_build_plate")
anchors anchors
{ {
bottom: parent.bottom; bottom: viewOrientationControls.top
left: parent.left; left: toolbar.right
margins: UM.Theme.getSize("default_margin").width
} }
} }
Topbar JobSpecs
{
id: jobSpecs
visible: CuraApplication.platformActivity
anchors
{
left: parent.left
bottom: viewOrientationControls.top
margins: UM.Theme.getSize("default_margin").width
bottomMargin: UM.Theme.getSize("thin_margin").width
}
}
ViewOrientationControls
{
id: viewOrientationControls
anchors
{
left: parent.left
bottom: parent.bottom
margins: UM.Theme.getSize("default_margin").width
}
}
Cura.ActionPanelWidget
{ {
id: topbar
anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.top: parent.top anchors.bottom: parent.bottom
anchors.rightMargin: UM.Theme.getSize("thick_margin").width
anchors.bottomMargin: UM.Theme.getSize("thick_margin").height
visible: CuraApplication.platformActivity
} }
Loader Loader
{ {
// A stage can control this area. If nothing is set, it will therefore show the 3D view.
id: main id: main
anchors anchors
{ {
top: topbar.bottom // Align to the top of the stageMenu since the stageMenu may not exist
bottom: parent.bottom top: parent.top
left: parent.left left: parent.left
right: sidebar.left right: parent.right
bottom: parent.bottom
} }
MouseArea source: UM.Controller.activeStage != null ? UM.Controller.activeStage.mainComponent : ""
{
visible: UM.Controller.activeStage.mainComponent != ""
anchors.fill: parent
acceptedButtons: Qt.AllButtons
onWheel: wheel.accepted = true
}
source: UM.Controller.activeStage.mainComponent
} }
Loader Loader
{ {
id: sidebar // The stage menu is, as the name implies, a menu that is defined by the active stage.
// Note that this menu does not need to be set at all! It's perfectly acceptable to have a stage
property bool collapsed: false; // without this menu!
property var initialWidth: UM.Theme.getSize("sidebar").width; id: stageMenu
function callExpandOrCollapse() {
if (collapsed) {
sidebar.visible = true;
sidebar.initialWidth = UM.Theme.getSize("sidebar").width;
viewportRect = Qt.rect(0, 0, (base.width - sidebar.width) / base.width, 1.0);
expandSidebarAnimation.start();
} else {
viewportRect = Qt.rect(0, 0, 1, 1.0);
collapseSidebarAnimation.start();
}
collapsed = !collapsed;
UM.Preferences.setValue("cura/sidebar_collapsed", collapsed);
}
anchors anchors
{ {
top: topbar.top left: parent.left
bottom: parent.bottom right: parent.right
top: parent.top
} }
width: initialWidth height: UM.Theme.getSize("stage_menu").height
x: base.width - sidebar.width source: UM.Controller.activeStage != null ? UM.Controller.activeStage.stageMenuComponent : ""
source: UM.Controller.activeStage.sidebarComponent
NumberAnimation { // The printSetupSelector is defined here so that the setting list doesn't need to get re-instantiated
id: collapseSidebarAnimation // Every time the stage is changed.
target: sidebar property var printSetupSelector: Cura.PrintSetupSelector
properties: "x"
to: base.width
duration: 100
}
NumberAnimation {
id: expandSidebarAnimation
target: sidebar
properties: "x"
to: base.width - sidebar.width
duration: 100
}
Component.onCompleted:
{ {
var sidebar_collapsed = UM.Preferences.getValue("cura/sidebar_collapsed"); width: UM.Theme.getSize("print_setup_widget").width
height: UM.Theme.getSize("stage_menu").height
if (sidebar_collapsed) headerCornerSide: RoundedRectangle.Direction.Right
{
sidebar.collapsed = true;
viewportRect = Qt.rect(0, 0, 1, 1.0)
collapseSidebarAnimation.start();
}
}
MouseArea
{
visible: UM.Controller.activeStage.sidebarComponent != ""
anchors.fill: parent
acceptedButtons: Qt.AllButtons
onWheel: wheel.accepted = true
} }
} }
@ -517,20 +293,17 @@ UM.MainWindow
anchors anchors
{ {
horizontalCenter: parent.horizontalCenter horizontalCenter: parent.horizontalCenter
horizontalCenterOffset: -(Math.round(UM.Theme.getSize("sidebar").width / 2)) top: parent.verticalCenter
top: parent.verticalCenter; bottom: parent.bottom
bottom: parent.bottom;
bottomMargin: UM.Theme.getSize("default_margin").height bottomMargin: UM.Theme.getSize("default_margin").height
} }
} }
} }
}
// Expand or collapse sidebar PrintSetupTooltip
Connections {
{ id: tooltip
target: Cura.Actions.expandSidebar }
onTriggered: sidebar.callExpandOrCollapse()
} }
UM.PreferencesDialog UM.PreferencesDialog
@ -567,13 +340,6 @@ UM.MainWindow
} }
} }
WorkspaceSummaryDialog
{
id: saveWorkspaceDialog
property var args
onYes: UM.OutputDeviceManager.requestWriteToDevice("local_file", PrintInformation.jobName, args)
}
Connections Connections
{ {
target: Cura.Actions.preferences target: Cura.Actions.preferences
@ -586,33 +352,6 @@ UM.MainWindow
onShowPreferencesWindow: preferences.visible = true onShowPreferencesWindow: preferences.visible = true
} }
MessageDialog
{
id: newProjectDialog
modality: Qt.ApplicationModal
title: catalog.i18nc("@title:window", "New project")
text: catalog.i18nc("@info:question", "Are you sure you want to start a new project? This will clear the build plate and any unsaved settings.")
standardButtons: StandardButton.Yes | StandardButton.No
icon: StandardIcon.Question
onYes:
{
CuraApplication.deleteAll();
Cura.Actions.resetProfile.trigger();
}
}
Connections
{
target: Cura.Actions.newProject
onTriggered:
{
if(Printer.platformActivity || Cura.MachineManager.hasUserSettings)
{
newProjectDialog.visible = true
}
}
}
Connections Connections
{ {
target: Cura.Actions.addProfile target: Cura.Actions.addProfile
@ -670,19 +409,6 @@ UM.MainWindow
} }
} }
UM.ExtensionModel {
id: curaExtensions
}
// show the plugin browser dialog
Connections
{
target: Cura.Actions.browsePackages
onTriggered: {
curaExtensions.callExtensionMethod("Toolbox", "browsePackages")
}
}
Timer Timer
{ {
id: createProfileTimer id: createProfileTimer
@ -703,7 +429,8 @@ UM.MainWindow
} }
} }
ContextMenu { ContextMenu
{
id: contextMenu id: contextMenu
} }
@ -870,7 +597,8 @@ UM.MainWindow
modality: Qt.ApplicationModal modality: Qt.ApplicationModal
} }
MessageDialog { MessageDialog
{
id: infoMultipleFilesWithGcodeDialog id: infoMultipleFilesWithGcodeDialog
title: catalog.i18nc("@title:window", "Open File(s)") title: catalog.i18nc("@title:window", "Open File(s)")
icon: StandardIcon.Information icon: StandardIcon.Information
@ -914,11 +642,6 @@ UM.MainWindow
} }
} }
EngineLog
{
id: engineLog;
}
Connections Connections
{ {
target: Cura.Actions.showProfileFolder target: Cura.Actions.showProfileFolder

View file

@ -0,0 +1,168 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Window 2.1
import UM 1.1 as UM
UM.Dialog
{
id: base
//: About dialog title
title: catalog.i18nc("@title:window","About Cura")
minimumWidth: 500 * screenScaleFactor
minimumHeight: 650 * screenScaleFactor
width: minimumWidth
height: minimumHeight
Rectangle
{
width: parent.width + 2 * margin // margin from Dialog.qml
height: version.y + version.height + margin
anchors.top: parent.top
anchors.topMargin: - margin
anchors.horizontalCenter: parent.horizontalCenter
color: UM.Theme.getColor("viewport_background")
}
Image
{
id: logo
width: (base.minimumWidth * 0.85) | 0
height: (width * (UM.Theme.getSize("logo").height / UM.Theme.getSize("logo").width)) | 0
source: UM.Theme.getImage("logo_about")
anchors.top: parent.top
anchors.topMargin: ((base.minimumWidth - width) / 2) | 0
anchors.horizontalCenter: parent.horizontalCenter
UM.I18nCatalog{id: catalog; name: "cura"}
}
Label
{
id: version
text: catalog.i18nc("@label","version: %1").arg(UM.Application.version)
font: UM.Theme.getFont("large")
color: UM.Theme.getColor("text")
anchors.right : logo.right
anchors.top: logo.bottom
anchors.topMargin: (UM.Theme.getSize("default_margin").height / 2) | 0
}
Label
{
id: description
width: parent.width
//: About dialog application description
text: catalog.i18nc("@label","End-to-end solution for fused filament 3D printing.")
font: UM.Theme.getFont("system")
wrapMode: Text.WordWrap
anchors.top: version.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
}
Label
{
id: creditsNotes
width: parent.width
//: About dialog application author note
text: catalog.i18nc("@info:credit","Cura is developed by Ultimaker B.V. in cooperation with the community.\nCura proudly uses the following open source projects:")
font: UM.Theme.getFont("system")
wrapMode: Text.WordWrap
anchors.top: description.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
}
ScrollView
{
id: credits
anchors.top: creditsNotes.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
width: parent.width
height: base.height - y - (2 * UM.Theme.getSize("default_margin").height + closeButton.height)
ListView
{
id: projectsList
width: parent.width
delegate: Row
{
Label
{
text: "<a href='%1' title='%2'>%2</a>".arg(model.url).arg(model.name)
width: (projectsList.width * 0.25) | 0
elide: Text.ElideRight
onLinkActivated: Qt.openUrlExternally(link)
}
Label
{
text: model.description
elide: Text.ElideRight
width: (projectsList.width * 0.6) | 0
}
Label
{
text: model.license
elide: Text.ElideRight
width: (projectsList.width * 0.15) | 0
}
}
model: ListModel
{
id: projectsModel
}
Component.onCompleted:
{
projectsModel.append({ name: "Cura", description: catalog.i18nc("@label", "Graphical user interface"), license: "LGPLv3", url: "https://github.com/Ultimaker/Cura" });
projectsModel.append({ name: "Uranium", description: catalog.i18nc("@label", "Application framework"), license: "LGPLv3", url: "https://github.com/Ultimaker/Uranium" });
projectsModel.append({ name: "CuraEngine", description: catalog.i18nc("@label", "G-code generator"), license: "AGPLv3", url: "https://github.com/Ultimaker/CuraEngine" });
projectsModel.append({ name: "libArcus", description: catalog.i18nc("@label", "Interprocess communication library"), license: "LGPLv3", url: "https://github.com/Ultimaker/libArcus" });
projectsModel.append({ name: "Python", description: catalog.i18nc("@label", "Programming language"), license: "Python", url: "http://python.org/" });
projectsModel.append({ name: "Qt5", description: catalog.i18nc("@label", "GUI framework"), license: "LGPLv3", url: "https://www.qt.io/" });
projectsModel.append({ name: "PyQt", description: catalog.i18nc("@label", "GUI framework bindings"), license: "GPL", url: "https://riverbankcomputing.com/software/pyqt" });
projectsModel.append({ name: "SIP", description: catalog.i18nc("@label", "C/C++ Binding library"), license: "GPL", url: "https://riverbankcomputing.com/software/sip" });
projectsModel.append({ name: "Protobuf", description: catalog.i18nc("@label", "Data interchange format"), license: "BSD", url: "https://developers.google.com/protocol-buffers" });
projectsModel.append({ name: "SciPy", description: catalog.i18nc("@label", "Support library for scientific computing"), license: "BSD-new", url: "https://www.scipy.org/" });
projectsModel.append({ name: "NumPy", description: catalog.i18nc("@label", "Support library for faster math"), license: "BSD", url: "http://www.numpy.org/" });
projectsModel.append({ name: "NumPy-STL", description: catalog.i18nc("@label", "Support library for handling STL files"), license: "BSD", url: "https://github.com/WoLpH/numpy-stl" });
projectsModel.append({ name: "Shapely", description: catalog.i18nc("@label", "Support library for handling planar objects"), license: "BSD", url: "https://github.com/Toblerity/Shapely" });
projectsModel.append({ name: "Trimesh", description: catalog.i18nc("@label", "Support library for handling triangular meshes"), license: "MIT", url: "https://trimsh.org" });
projectsModel.append({ name: "NetworkX", description: catalog.i18nc("@label", "Support library for analysis of complex networks"), license: "3-clause BSD", url: "https://networkx.github.io/" });
projectsModel.append({ name: "libSavitar", description: catalog.i18nc("@label", "Support library for handling 3MF files"), license: "LGPLv3", url: "https://github.com/ultimaker/libsavitar" });
projectsModel.append({ name: "libCharon", description: catalog.i18nc("@label", "Support library for file metadata and streaming"), license: "LGPLv3", url: "https://github.com/ultimaker/libcharon" });
projectsModel.append({ name: "PySerial", description: catalog.i18nc("@label", "Serial communication library"), license: "Python", url: "http://pyserial.sourceforge.net/" });
projectsModel.append({ name: "python-zeroconf", description: catalog.i18nc("@label", "ZeroConf discovery library"), license: "LGPL", url: "https://github.com/jstasiak/python-zeroconf" });
projectsModel.append({ name: "Clipper", description: catalog.i18nc("@label", "Polygon clipping library"), license: "Boost", url: "http://www.angusj.com/delphi/clipper.php" });
projectsModel.append({ name: "Requests", description: catalog.i18nc("@Label", "Python HTTP library"), license: "GPL", url: "http://docs.python-requests.org" });
projectsModel.append({ name: "Noto Sans", description: catalog.i18nc("@label", "Font"), license: "Apache 2.0", url: "https://www.google.com/get/noto/" });
projectsModel.append({ name: "Font-Awesome-SVG-PNG", description: catalog.i18nc("@label", "SVG icons"), license: "SIL OFL 1.1", url: "https://github.com/encharm/Font-Awesome-SVG-PNG" });
projectsModel.append({ name: "AppImageKit", description: catalog.i18nc("@label", "Linux cross-distribution application deployment"), license: "MIT", url: "https://github.com/AppImage/AppImageKit" });
}
}
}
rightButtons: Button
{
//: Close about dialog button
id: closeButton
text: catalog.i18nc("@action:button","Close");
onClicked: base.visible = false;
}
}

View file

@ -1,4 +1,4 @@
// Copyright (c) 2017 Ultimaker B.V. // Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher. // Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.2 import QtQuick 2.2
@ -156,7 +156,6 @@ UM.Dialog
anchors.rightMargin: UM.Theme.getSize("default_margin").width anchors.rightMargin: UM.Theme.getSize("default_margin").width
width: UM.Theme.getSize("standard_arrow").width width: UM.Theme.getSize("standard_arrow").width
height: UM.Theme.getSize("standard_arrow").height height: UM.Theme.getSize("standard_arrow").height
sourceSize.width: width
sourceSize.height: width sourceSize.height: width
color: palette.windowText color: palette.windowText
source: base.activeCategory == section ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_right") source: base.activeCategory == section ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_right")
@ -170,7 +169,7 @@ UM.Dialog
if (machineList.model.getItem(machineList.currentIndex).section != section) if (machineList.model.getItem(machineList.currentIndex).section != section)
{ {
// Find the first machine from this section // Find the first machine from this section
for(var i = 0; i < machineList.model.rowCount(); i++) for(var i = 0; i < machineList.model.count; i++)
{ {
var item = machineList.model.getItem(i); var item = machineList.model.getItem(i);
if (item.section == section) if (item.section == section)
@ -276,7 +275,6 @@ UM.Dialog
id: machineName id: machineName
text: getMachineName() text: getMachineName()
width: Math.floor(parent.width * 0.75) width: Math.floor(parent.width * 0.75)
implicitWidth: UM.Theme.getSize("standard_list_input").width
maximumLength: 40 maximumLength: 40
//validator: Cura.MachineNameValidator { } //TODO: Gives a segfault in PyQt5.6. For now, we must use a signal on text changed. //validator: Cura.MachineNameValidator { } //TODO: Gives a segfault in PyQt5.6. For now, we must use a signal on text changed.
validator: RegExpValidator validator: RegExpValidator

View file

@ -1,53 +0,0 @@
// Copyright (c) 2015 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1
import UM 1.1 as UM
UM.Dialog
{
id: dialog;
//: Engine Log dialog title
title: catalog.i18nc("@title:window","Engine Log");
modality: Qt.NonModal;
TextArea
{
id: textArea
anchors.fill: parent;
Timer
{
id: updateTimer;
interval: 1000;
running: false;
repeat: true;
onTriggered: textArea.text = CuraApplication.getEngineLog();
}
UM.I18nCatalog{id: catalog; name:"cura"}
}
rightButtons: Button
{
//: Close engine log button
text: catalog.i18nc("@action:button","Close");
onClicked: dialog.visible = false;
}
onVisibleChanged:
{
if(visible)
{
textArea.text = CuraApplication.getEngineLog();
updateTimer.start();
} else
{
updateTimer.stop();
}
}
}

View file

@ -0,0 +1,262 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.7
import QtQuick.Controls 2.3
import UM 1.2 as UM
import Cura 1.0 as Cura
import QtGraphicalEffects 1.0 // For the dropshadow
// The expandable component has 2 major sub components:
// * The headerItem; Always visible and should hold some info about what happens if the component is expanded
// * The contentItem; The content that needs to be shown if the component is expanded.
Item
{
id: base
// Enumeration with the different possible alignments of the content with respect of the headerItem
enum ContentAlignment
{
AlignLeft,
AlignRight
}
// The headerItem holds the QML item that is always displayed.
property alias headerItem: headerItemLoader.sourceComponent
// The contentItem holds the QML item that is shown when the "open" button is pressed
property alias contentItem: content.contentItem
property color contentBackgroundColor: UM.Theme.getColor("action_button")
property color headerBackgroundColor: UM.Theme.getColor("action_button")
property color headerActiveColor: UM.Theme.getColor("secondary")
property color headerHoverColor: UM.Theme.getColor("action_button_hovered")
property alias enabled: mouseArea.enabled
// Text to show when this component is disabled
property alias disabledText: disabledLabel.text
// Defines the alignment of the content with respect of the headerItem, by default to the right
property int contentAlignment: ExpandableComponent.ContentAlignment.AlignRight
// How much spacing is needed around the contentItem
property alias contentPadding: content.padding
// Adds a title to the content item
property alias contentHeaderTitle: contentHeader.headerTitle
// How much spacing is needed for the contentItem by Y coordinate
property var contentSpacingY: UM.Theme.getSize("narrow_margin").width
// How much padding is needed around the header & button
property alias headerPadding: background.padding
// What icon should be displayed on the right.
property alias iconSource: collapseButton.source
property alias iconColor: collapseButton.color
// The icon size (it's always drawn as a square)
property alias iconSize: collapseButton.height
// Is the "drawer" open?
readonly property alias expanded: contentContainer.visible
// What should the radius of the header be. This is also influenced by the headerCornerSide
property alias headerRadius: background.radius
// On what side should the header corners be shown? 1 is down, 2 is left, 3 is up and 4 is right.
property alias headerCornerSide: background.cornerSide
property alias headerShadowColor: shadow.color
property alias enableHeaderShadow: shadow.visible
property int shadowOffset: 2
function toggleContent()
{
contentContainer.visible = !expanded
}
// Add this binding since the background color is not updated otherwise
Binding
{
target: background
property: "color"
value:
{
return base.enabled ? (expanded ? headerActiveColor : headerBackgroundColor) : UM.Theme.getColor("disabled")
}
}
// The panel needs to close when it becomes disabled
Connections
{
target: base
onEnabledChanged:
{
if (!base.enabled && expanded)
{
toggleContent()
}
}
}
implicitHeight: 100 * screenScaleFactor
implicitWidth: 400 * screenScaleFactor
RoundedRectangle
{
id: background
property real padding: UM.Theme.getSize("default_margin").width
color: base.enabled ? (base.expanded ? headerActiveColor : headerBackgroundColor) : UM.Theme.getColor("disabled")
anchors.fill: parent
Label
{
id: disabledLabel
visible: !base.enabled
anchors.fill: parent
leftPadding: background.padding
rightPadding: background.padding
text: ""
font: UM.Theme.getFont("default")
renderType: Text.NativeRendering
verticalAlignment: Text.AlignVCenter
color: UM.Theme.getColor("text")
wrapMode: Text.WordWrap
}
Item
{
anchors.fill: parent
visible: base.enabled
Loader
{
id: headerItemLoader
anchors
{
left: parent.left
right: collapseButton.visible ? collapseButton.left : parent.right
top: parent.top
bottom: parent.bottom
margins: background.padding
}
}
UM.RecolorImage
{
id: collapseButton
anchors
{
right: parent.right
verticalCenter: parent.verticalCenter
margins: background.padding
}
source: UM.Theme.getIcon("pencil")
visible: source != ""
width: UM.Theme.getSize("standard_arrow").width
height: UM.Theme.getSize("standard_arrow").height
color: UM.Theme.getColor("small_button_text")
}
}
MouseArea
{
id: mouseArea
anchors.fill: parent
onClicked: toggleContent()
hoverEnabled: true
onEntered: background.color = headerHoverColor
onExited: background.color = base.enabled ? (base.expanded ? headerActiveColor : headerBackgroundColor) : UM.Theme.getColor("disabled")
}
}
DropShadow
{
id: shadow
// Don't blur the shadow
radius: 0
anchors.fill: background
source: background
verticalOffset: base.shadowOffset
visible: true
color: UM.Theme.getColor("action_button_shadow")
// Should always be drawn behind the background.
z: background.z - 1
}
Cura.RoundedRectangle
{
id: contentContainer
visible: false
width: childrenRect.width
height: childrenRect.height
// Ensure that the content is located directly below the headerItem
y: background.height + base.shadowOffset + base.contentSpacingY
// Make the content aligned with the rest, using the property contentAlignment to decide whether is right or left.
// In case of right alignment, the 3x padding is due to left, right and padding between the button & text.
x: contentAlignment == ExpandableComponent.ContentAlignment.AlignRight ? -width + collapseButton.width + headerItemLoader.width + 3 * background.padding : 0
cornerSide: Cura.RoundedRectangle.Direction.All
color: contentBackgroundColor
border.width: UM.Theme.getSize("default_lining").width
border.color: UM.Theme.getColor("lining")
radius: UM.Theme.getSize("default_radius").width
ExpandableComponentHeader
{
id: contentHeader
headerTitle: ""
anchors
{
top: parent.top
right: parent.right
left: parent.left
}
}
Control
{
id: content
anchors.top: contentHeader.bottom
padding: UM.Theme.getSize("default_margin").width
contentItem: Item {}
onContentItemChanged:
{
// Since we want the size of the content to be set by the size of the content,
// we need to do it like this.
content.width = contentItem.width + 2 * content.padding
content.height = contentItem.height + 2 * content.padding
}
}
}
// DO NOT MOVE UP IN THE CODE: This connection has to be here, after the definition of the content item.
// Apparently the order in which these are handled matters and so the height is correctly updated if this is here.
Connections
{
// Since it could be that the content is dynamically populated, we should also take these changes into account.
target: content.contentItem
onWidthChanged: content.width = content.contentItem.width + 2 * content.padding
onHeightChanged:
{
content.height = content.contentItem.height + 2 * content.padding
contentContainer.height = contentHeader.height + content.height
}
}
}

View file

@ -0,0 +1,68 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.7
import QtQuick.Controls 2.3
import UM 1.2 as UM
import Cura 1.0 as Cura
// Header of the popup
Cura.RoundedRectangle
{
id: header
property alias headerTitle: headerLabel.text
height: UM.Theme.getSize("expandable_component_content_header").height
color: UM.Theme.getColor("secondary")
cornerSide: Cura.RoundedRectangle.Direction.Up
border.width: UM.Theme.getSize("default_lining").width
border.color: UM.Theme.getColor("lining")
radius: UM.Theme.getSize("default_radius").width
Label
{
id: headerLabel
text: ""
font: UM.Theme.getFont("default")
renderType: Text.NativeRendering
verticalAlignment: Text.AlignVCenter
color: UM.Theme.getColor("small_button_text")
height: parent.height
anchors
{
topMargin: UM.Theme.getSize("default_margin").height
left: parent.left
leftMargin: UM.Theme.getSize("default_margin").height
}
}
Button
{
id: closeButton
width: UM.Theme.getSize("message_close").width
height: UM.Theme.getSize("message_close").height
hoverEnabled: true
anchors
{
right: parent.right
rightMargin: UM.Theme.getSize("default_margin").width
verticalCenter: parent.verticalCenter
}
contentItem: UM.RecolorImage
{
anchors.fill: parent
sourceSize.width: width
color: closeButton.hovered ? UM.Theme.getColor("small_button_text_hover") : UM.Theme.getColor("small_button_text")
source: UM.Theme.getIcon("cross1")
}
background: Item {}
onClicked: toggleContent() // Will hide the popup item
}
}

View file

@ -0,0 +1,250 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.7
import QtQuick.Controls 2.3
import UM 1.2 as UM
import Cura 1.0 as Cura
import QtGraphicalEffects 1.0 // For the dropshadow
// The expandable component has 2 major sub components:
// * The headerItem; Always visible and should hold some info about what happens if the component is expanded
// * The contentItem; The content that needs to be shown if the component is expanded.
Item
{
id: base
// Enumeration with the different possible alignments of the content with respect of the headerItem
enum ContentAlignment
{
AlignLeft,
AlignRight
}
// The headerItem holds the QML item that is always displayed.
property alias headerItem: headerItemLoader.sourceComponent
// The contentItem holds the QML item that is shown when the "open" button is pressed
property alias contentItem: content.contentItem
property color contentBackgroundColor: UM.Theme.getColor("action_button")
property color headerBackgroundColor: UM.Theme.getColor("action_button")
property color headerActiveColor: UM.Theme.getColor("secondary")
property color headerHoverColor: UM.Theme.getColor("action_button_hovered")
property alias enabled: mouseArea.enabled
// Text to show when this component is disabled
property alias disabledText: disabledLabel.text
// Defines the alignment of the content with respect of the headerItem, by default to the right
property int contentAlignment: ExpandablePopup.ContentAlignment.AlignRight
// How much spacing is needed around the contentItem
property alias contentPadding: content.padding
// How much padding is needed around the header & button
property alias headerPadding: background.padding
// What icon should be displayed on the right.
property alias iconSource: collapseButton.source
property alias iconColor: collapseButton.color
// The icon size (it's always drawn as a square)
property alias iconSize: collapseButton.height
// Is the "drawer" open?
readonly property alias expanded: content.visible
property alias expandedHighlightColor: expandedHighlight.color
// What should the radius of the header be. This is also influenced by the headerCornerSide
property alias headerRadius: background.radius
// On what side should the header corners be shown? 1 is down, 2 is left, 3 is up and 4 is right.
property alias headerCornerSide: background.cornerSide
// Change the contentItem close behaviour
property alias contentClosePolicy : content.closePolicy
property alias headerShadowColor: shadow.color
property alias enableHeaderShadow: shadow.visible
property int shadowOffset: 2
function toggleContent()
{
if (content.visible)
{
content.close()
}
else
{
content.open()
}
}
// Add this binding since the background color is not updated otherwise
Binding
{
target: background
property: "color"
value: base.enabled ? headerBackgroundColor : UM.Theme.getColor("disabled")
}
// The panel needs to close when it becomes disabled
Connections
{
target: base
onEnabledChanged:
{
if (!base.enabled && expanded)
{
toggleContent()
}
}
}
implicitHeight: 100 * screenScaleFactor
implicitWidth: 400 * screenScaleFactor
RoundedRectangle
{
id: background
property real padding: UM.Theme.getSize("default_margin").width
color: base.enabled ? headerBackgroundColor : UM.Theme.getColor("disabled")
anchors.fill: parent
Label
{
id: disabledLabel
visible: !base.enabled
leftPadding: background.padding
text: ""
font: UM.Theme.getFont("default")
renderType: Text.NativeRendering
verticalAlignment: Text.AlignVCenter
color: UM.Theme.getColor("text")
height: parent.height
}
Item
{
anchors.fill: parent
visible: base.enabled
Loader
{
id: headerItemLoader
anchors
{
left: parent.left
right: collapseButton.visible ? collapseButton.left : parent.right
top: parent.top
bottom: parent.bottom
margins: background.padding
}
}
// A highlight that is shown when the content is expanded
Rectangle
{
id: expandedHighlight
width: parent.width
height: UM.Theme.getSize("thick_lining").height
color: UM.Theme.getColor("primary")
visible: expanded
anchors.bottom: parent.bottom
}
UM.RecolorImage
{
id: collapseButton
anchors
{
right: parent.right
verticalCenter: parent.verticalCenter
margins: background.padding
}
source: expanded ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left")
visible: source != ""
width: UM.Theme.getSize("standard_arrow").width
height: UM.Theme.getSize("standard_arrow").height
color: UM.Theme.getColor("small_button_text")
}
}
MouseArea
{
id: mouseArea
anchors.fill: parent
onClicked: toggleContent()
hoverEnabled: true
onEntered: background.color = headerHoverColor
onExited: background.color = base.enabled ? headerBackgroundColor : UM.Theme.getColor("disabled")
}
}
DropShadow
{
id: shadow
// Don't blur the shadow
radius: 0
anchors.fill: background
source: background
verticalOffset: base.shadowOffset
visible: true
color: UM.Theme.getColor("action_button_shadow")
// Should always be drawn behind the background.
z: background.z - 1
}
Popup
{
id: content
// Ensure that the content is located directly below the headerItem
y: background.height + base.shadowOffset
// Make the content aligned with the rest, using the property contentAlignment to decide whether is right or left.
// In case of right alignment, the 3x padding is due to left, right and padding between the button & text.
x: contentAlignment == ExpandablePopup.ContentAlignment.AlignRight ? -width + collapseButton.width + headerItemLoader.width + 3 * background.padding : 0
padding: UM.Theme.getSize("default_margin").width
closePolicy: Popup.CloseOnPressOutsideParent
background: Cura.RoundedRectangle
{
cornerSide: Cura.RoundedRectangle.Direction.Down
color: contentBackgroundColor
border.width: UM.Theme.getSize("default_lining").width
border.color: UM.Theme.getColor("lining")
radius: UM.Theme.getSize("default_radius").width
}
contentItem: Item {}
onContentItemChanged:
{
// Since we want the size of the content to be set by the size of the content,
// we need to do it like this.
content.width = contentItem.width + 2 * content.padding
content.height = contentItem.height + 2 * content.padding
}
}
// DO NOT MOVE UP IN THE CODE: This connection has to be here, after the definition of the content item.
// Apparently the order in which these are handled matters and so the height is correctly updated if this is here.
Connections
{
// Since it could be that the content is dynamically populated, we should also take these changes into account.
target: content.contentItem
onWidthChanged: content.width = content.contentItem.width + 2 * content.padding
onHeightChanged: content.height = content.contentItem.height + 2 * content.padding
}
}

View file

@ -2,80 +2,32 @@
// Cura is released under the terms of the LGPLv3 or higher. // Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.2 import QtQuick 2.2
import QtQuick.Controls 1.1 import QtQuick.Controls 2.0
import UM 1.2 as UM import UM 1.2 as UM
import Cura 1.0 as Cura import Cura 1.0 as Cura
Button Cura.ToolbarButton
{ {
id: base id: base
property var extruder; property var extruder
text: catalog.i18ncp("@label %1 is filled in with the name of an extruder", "Print Selected Model with %1", "Print Selected Models with %1", UM.Selection.selectionCount).arg(extruder.name) text: catalog.i18ncp("@label %1 is filled in with the name of an extruder", "Print Selected Model with %1", "Print Selected Models with %1", UM.Selection.selectionCount).arg(extruder.name)
style: UM.Theme.styles.tool_button;
iconSource: UM.Theme.getIcon("extruder_button")
checked: Cura.ExtruderManager.selectedObjectExtruders.indexOf(extruder.id) != -1 checked: Cura.ExtruderManager.selectedObjectExtruders.indexOf(extruder.id) != -1
enabled: UM.Selection.hasSelection && extruder.stack.isEnabled enabled: UM.Selection.hasSelection && extruder.stack.isEnabled
property color customColor: base.hovered ? UM.Theme.getColor("button_hover") : UM.Theme.getColor("button"); toolItem: ExtruderIcon
Rectangle
{ {
anchors.fill: parent materialColor: extruder.color
anchors.margins: UM.Theme.getSize("default_lining").width; extruderEnabled: extruder.stack.isEnabled
property int index: extruder.index
color: "transparent"
border.width: base.checked ? UM.Theme.getSize("default_lining").width : 0;
border.color: UM.Theme.getColor("button_text")
}
Item
{
anchors.centerIn: parent
width: UM.Theme.getSize("default_margin").width
height: UM.Theme.getSize("default_margin").height
Label
{
anchors.centerIn: parent;
text: index + 1;
color: parent.enabled ? UM.Theme.getColor("button_text") : UM.Theme.getColor("button_disabled_text")
font: UM.Theme.getFont("default_bold");
}
}
// Material colour circle
// Only draw the filling colour of the material inside the SVG border.
Rectangle
{
anchors
{
right: parent.right
top: parent.top
rightMargin: UM.Theme.getSize("extruder_button_material_margin").width
topMargin: UM.Theme.getSize("extruder_button_material_margin").height
}
color: model.color
width: UM.Theme.getSize("extruder_button_material").width
height: UM.Theme.getSize("extruder_button_material").height
radius: Math.round(width / 2)
border.width: UM.Theme.getSize("default_lining").width
border.color: UM.Theme.getColor("extruder_button_material_border")
opacity: !base.enabled ? 0.2 : 1.0
} }
onClicked: onClicked:
{ {
forceActiveFocus() //First grab focus, so all the text fields are updated forceActiveFocus() //First grab focus, so all the text fields are updated
CuraActions.setExtruderForSelection(extruder.id); CuraActions.setExtruderForSelection(extruder.id)
} }
} }

View file

@ -0,0 +1,71 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.7
import QtQuick.Controls 1.1
import UM 1.2 as UM
Item
{
id: extruderIconItem
implicitWidth: UM.Theme.getSize("extruder_icon").width
implicitHeight: UM.Theme.getSize("extruder_icon").height
property bool checked: true
property color materialColor
property alias textColor: extruderNumberText.color
property bool extruderEnabled: true
UM.RecolorImage
{
id: mainIcon
anchors.fill: parent
source: UM.Theme.getIcon("extruder_button")
color: extruderEnabled ? materialColor: UM.Theme.getColor("disabled")
}
Rectangle
{
id: extruderNumberCircle
width: height
height: Math.round(parent.height / 2)
radius: Math.round(width / 2)
color: UM.Theme.getColor("toolbar_background")
anchors
{
horizontalCenter: parent.horizontalCenter
top: parent.top
// The circle needs to be slightly off center (so it sits in the middle of the square bit of the icon)
topMargin: (parent.height - height) / 2 - 0.1 * parent.height
}
Label
{
id: extruderNumberText
anchors.centerIn: parent
text: index + 1
font: UM.Theme.getFont("very_small")
width: contentWidth
height: contentHeight
visible: extruderEnabled
renderType: Text.NativeRendering
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
UM.RecolorImage
{
id: disabledIcon
anchors.fill: parent
anchors.margins: UM.Theme.getSize("thick_lining").width
sourceSize.height: width
source: UM.Theme.getIcon("cross1")
visible: !extruderEnabled
color: "black"
}
}
}

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