diff --git a/README.md b/README.md
index 70466e9c22..93abcc0c61 100644
--- a/README.md
+++ b/README.md
@@ -20,8 +20,9 @@ Dependencies
------------
* [Uranium](https://github.com/Ultimaker/Uranium) Cura is built on top of the Uranium framework.
* [CuraEngine](https://github.com/Ultimaker/CuraEngine) This will be needed at runtime to perform the actual slicing.
+* [fdm_materials](https://github.com/Ultimaker/fdm_materials) Required to load a printer that has swappable material profiles.
* [PySerial](https://github.com/pyserial/pyserial) Only required for USB printing support.
-* [python-zeroconf](https://github.com/jstasiak/python-zeroconf) Only required to detect mDNS-enabled printers
+* [python-zeroconf](https://github.com/jstasiak/python-zeroconf) Only required to detect mDNS-enabled printers.
Build scripts
-------------
diff --git a/cura.desktop.in b/cura.desktop.in
index fbe8b30fed..b0195015a5 100644
--- a/cura.desktop.in
+++ b/cura.desktop.in
@@ -13,6 +13,6 @@ TryExec=@CMAKE_INSTALL_FULL_BINDIR@/cura
Icon=cura-icon
Terminal=false
Type=Application
-MimeType=model/stl;application/vnd.ms-3mfdocument;application/prs.wavefront-obj;image/bmp;image/gif;image/jpeg;image/png;model/x3d+xml;
+MimeType=model/stl;application/vnd.ms-3mfdocument;application/prs.wavefront-obj;image/bmp;image/gif;image/jpeg;image/png;model/x3d+xml;text/x-gcode;
Categories=Graphics;
Keywords=3D;Printing;Slicer;
diff --git a/cura.sharedmimeinfo b/cura.sharedmimeinfo
index 23d38795eb..ed9099d425 100644
--- a/cura.sharedmimeinfo
+++ b/cura.sharedmimeinfo
@@ -19,4 +19,12 @@
+
+
+ Gcode file
+
+
+
+
+
\ No newline at end of file
diff --git a/cura/Backups/Backup.py b/cura/Backups/Backup.py
index 897d5fa979..714d6527fe 100644
--- a/cura/Backups/Backup.py
+++ b/cura/Backups/Backup.py
@@ -46,12 +46,13 @@ class Backup:
# We copy the preferences file to the user data directory in Linux as it's in a different location there.
# When restoring a backup on Linux, we move it back.
- if Platform.isLinux():
+ if Platform.isLinux(): #TODO: This should check for the config directory not being the same as the data directory, rather than hard-coding that to Linux systems.
preferences_file_name = self._application.getApplicationName()
preferences_file = Resources.getPath(Resources.Preferences, "{}.cfg".format(preferences_file_name))
backup_preferences_file = os.path.join(version_data_dir, "{}.cfg".format(preferences_file_name))
- Logger.log("d", "Copying preferences file from %s to %s", preferences_file, backup_preferences_file)
- shutil.copyfile(preferences_file, backup_preferences_file)
+ if os.path.exists(preferences_file) and (not os.path.exists(backup_preferences_file) or not os.path.samefile(preferences_file, backup_preferences_file)):
+ Logger.log("d", "Copying preferences file from %s to %s", preferences_file, backup_preferences_file)
+ shutil.copyfile(preferences_file, backup_preferences_file)
# Create an empty buffer and write the archive to it.
buffer = io.BytesIO()
diff --git a/cura/CuraActions.py b/cura/CuraActions.py
index 93a18318df..49f7e740a9 100644
--- a/cura/CuraActions.py
+++ b/cura/CuraActions.py
@@ -3,7 +3,7 @@
from PyQt5.QtCore import QObject, QUrl
from PyQt5.QtGui import QDesktopServices
-from typing import List, TYPE_CHECKING
+from typing import List, TYPE_CHECKING, cast
from UM.Event import CallFunctionEvent
from UM.FlameProfiler import pyqtSlot
@@ -61,8 +61,10 @@ class CuraActions(QObject):
operation = GroupedOperation()
for node in Selection.getAllSelectedObjects():
current_node = node
- while current_node.getParent() and current_node.getParent().callDecoration("isGroup"):
- current_node = current_node.getParent()
+ parent_node = current_node.getParent()
+ while parent_node and parent_node.callDecoration("isGroup"):
+ current_node = parent_node
+ parent_node = current_node.getParent()
# This was formerly done with SetTransformOperation but because of
# unpredictable matrix deconstruction it was possible that mirrors
@@ -150,13 +152,13 @@ class CuraActions(QObject):
root = cura.CuraApplication.CuraApplication.getInstance().getController().getScene().getRoot()
- nodes_to_change = []
+ nodes_to_change = [] # type: List[SceneNode]
for node in Selection.getAllSelectedObjects():
parent_node = node # Find the parent node to change instead
while parent_node.getParent() != root:
- parent_node = parent_node.getParent()
+ parent_node = cast(SceneNode, parent_node.getParent())
- for single_node in BreadthFirstIterator(parent_node): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
+ for single_node in BreadthFirstIterator(parent_node): # type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
nodes_to_change.append(single_node)
if not nodes_to_change:
diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py
index 6e52a97518..b43e4d5e0e 100755
--- a/cura/CuraApplication.py
+++ b/cura/CuraApplication.py
@@ -1663,7 +1663,9 @@ class CuraApplication(QtApplication):
is_non_sliceable = "." + file_extension in self._non_sliceable_extensions
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()
node.addDecorator(block_slicing_decorator)
diff --git a/cura/PrintInformation.py b/cura/PrintInformation.py
index e11f70a54c..e863689e21 100644
--- a/cura/PrintInformation.py
+++ b/cura/PrintInformation.py
@@ -14,8 +14,7 @@ from UM.Logger import Logger
from UM.Qt.Duration import Duration
from UM.Scene.SceneNode import SceneNode
from UM.i18n import i18nCatalog
-from UM.MimeTypeDatabase import MimeTypeDatabase
-
+from UM.MimeTypeDatabase import MimeTypeDatabase, MimeTypeNotFoundError
from typing import TYPE_CHECKING
@@ -361,7 +360,7 @@ class PrintInformation(QObject):
try:
mime_type = MimeTypeDatabase.getMimeTypeForFile(name)
data = mime_type.stripExtension(name)
- except:
+ except MimeTypeNotFoundError:
Logger.log("w", "Unsupported Mime Type Database file extension %s", name)
if data is not None and check_name is not None:
@@ -395,28 +394,14 @@ class PrintInformation(QObject):
return
active_machine_type_name = global_container_stack.definition.getName()
- abbr_machine = ""
- 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
+ self._abbr_machine = self._application.getMachineManager().getAbbreviatedMachineName(active_machine_type_name)
## Utility method that strips accents from characters (eg: รข -> a)
def _stripAccents(self, to_strip: str) -> str:
return ''.join(char for char in unicodedata.normalize('NFD', to_strip) if unicodedata.category(char) != 'Mn')
@pyqtSlot(result = "QVariantMap")
- def getFeaturePrintTimes(self):
+ def getFeaturePrintTimes(self) -> Dict[str, Duration]:
result = {}
if self._active_build_plate not in self._print_times_per_feature:
self._initPrintTimesPerFeature(self._active_build_plate)
diff --git a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py
index 35d2ce014a..9a3be936a2 100644
--- a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py
+++ b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py
@@ -147,6 +147,9 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice):
request.setHeader(QNetworkRequest.UserAgentHeader, self._user_agent)
return request
+ def createFormPart(self, content_header: str, data: bytes, content_type: Optional[str] = None) -> QHttpPart:
+ return self._createFormPart(content_header, data, content_type)
+
def _createFormPart(self, content_header: str, data: bytes, content_type: Optional[str] = None) -> QHttpPart:
part = QHttpPart()
diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py
index 969aa3c460..99c48189cc 100644
--- a/cura/PrinterOutputDevice.py
+++ b/cura/PrinterOutputDevice.py
@@ -211,6 +211,11 @@ class PrinterOutputDevice(QObject, OutputDevice):
self._unique_configurations.sort(key = lambda k: k.printerType)
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:
for printer in self._printers:
printer.configurationChanged.connect(self._updateUniqueConfigurations)
@@ -238,4 +243,4 @@ class PrinterOutputDevice(QObject, OutputDevice):
if not self._firmware_updater:
return
- self._firmware_updater.updateFirmware(firmware_file)
\ No newline at end of file
+ self._firmware_updater.updateFirmware(firmware_file)
diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py
index f321ce94a6..e3d7eedd9a 100755
--- a/cura/Settings/MachineManager.py
+++ b/cura/Settings/MachineManager.py
@@ -3,6 +3,8 @@
import collections
import time
+import re
+import unicodedata
from typing import Any, Callable, List, Dict, TYPE_CHECKING, Optional, cast
from UM.ConfigurationErrorMessage import ConfigurationErrorMessage
@@ -64,7 +66,7 @@ class MachineManager(QObject):
self.machine_extruder_material_update_dict = collections.defaultdict(list) #type: Dict[str, List[Callable[[], None]]]
- self._instance_container_timer = QTimer() #type: QTimer
+ self._instance_container_timer = QTimer() # type: QTimer
self._instance_container_timer.setInterval(250)
self._instance_container_timer.setSingleShot(True)
self._instance_container_timer.timeout.connect(self.__emitChangedSignals)
@@ -74,7 +76,7 @@ class MachineManager(QObject):
self._application.globalContainerStackChanged.connect(self._onGlobalContainerChanged)
self._container_registry.containerLoadComplete.connect(self._onContainersChanged)
- ## When the global container is changed, active material probably needs to be updated.
+ # When the global container is changed, active material probably needs to be updated.
self.globalContainerChanged.connect(self.activeMaterialChanged)
self.globalContainerChanged.connect(self.activeVariantChanged)
self.globalContainerChanged.connect(self.activeQualityChanged)
@@ -115,15 +117,15 @@ class MachineManager(QObject):
self._material_incompatible_message = Message(catalog.i18nc("@info:status",
"The selected material is incompatible with the selected machine or configuration."),
- title = catalog.i18nc("@info:title", "Incompatible Material")) #type: Message
+ title = catalog.i18nc("@info:title", "Incompatible Material")) # type: Message
- containers = CuraContainerRegistry.getInstance().findInstanceContainers(id = self.activeMaterialId) #type: List[InstanceContainer]
+ containers = CuraContainerRegistry.getInstance().findInstanceContainers(id = self.activeMaterialId) # type: List[InstanceContainer]
if containers:
containers[0].nameChanged.connect(self._onMaterialNameChanged)
- self._material_manager = self._application.getMaterialManager() #type: MaterialManager
- self._variant_manager = self._application.getVariantManager() #type: VariantManager
- self._quality_manager = self._application.getQualityManager() #type: QualityManager
+ self._material_manager = self._application.getMaterialManager() # type: MaterialManager
+ self._variant_manager = self._application.getVariantManager() # type: VariantManager
+ self._quality_manager = self._application.getQualityManager() # type: QualityManager
# When the materials lookup table gets updated, it can mean that a material has its name changed, which should
# be reflected on the GUI. This signal emission makes sure that it happens.
@@ -156,7 +158,7 @@ class MachineManager(QObject):
blurSettings = pyqtSignal() # Emitted to force fields in the advanced sidebar to un-focus, so they update properly
outputDevicesChanged = pyqtSignal()
- currentConfigurationChanged = pyqtSignal() # Emitted every time the current configurations of the machine changes
+ currentConfigurationChanged = pyqtSignal() # Emitted every time the current configurations of the machine changes
printerConnectedStatusChanged = pyqtSignal() # Emitted every time the active machine change or the outputdevices change
rootMaterialChanged = pyqtSignal()
@@ -201,7 +203,7 @@ class MachineManager(QObject):
extruder_configuration.hotendID = extruder.variant.getName() if extruder.variant != empty_variant_container else None
self._current_printer_configuration.extruderConfigurations.append(extruder_configuration)
- # an empty build plate configuration from the network printer is presented as an empty string, so use "" for an
+ # An empty build plate configuration from the network printer is presented as an empty string, so use "" for an
# empty build plate.
self._current_printer_configuration.buildplateConfiguration = self._global_container_stack.getProperty("machine_buildplate_type", "value") if self._global_container_stack.variant != empty_variant_container else ""
self.currentConfigurationChanged.emit()
@@ -247,7 +249,7 @@ class MachineManager(QObject):
self.updateNumberExtrudersEnabled()
self.globalContainerChanged.emit()
- # after switching the global stack we reconnect all the signals and set the variant and material references
+ # After switching the global stack we reconnect all the signals and set the variant and material references
if self._global_container_stack:
self._application.getPreferences().setValue("cura/active_machine", self._global_container_stack.getId())
@@ -261,7 +263,7 @@ class MachineManager(QObject):
if global_variant.getMetaDataEntry("hardware_type") != "buildplate":
self._global_container_stack.setVariant(empty_variant_container)
- # set the global material to empty as we now use the extruder stack at all times - CURA-4482
+ # Set the global material to empty as we now use the extruder stack at all times - CURA-4482
global_material = self._global_container_stack.material
if global_material != empty_material_container:
self._global_container_stack.setMaterial(empty_material_container)
@@ -419,7 +421,7 @@ class MachineManager(QObject):
# Not a very pretty solution, but the extruder manager doesn't really know how many extruders there are
machine_extruder_count = self._global_container_stack.getProperty("machine_extruder_count", "value")
extruder_stacks = ExtruderManager.getInstance().getActiveExtruderStacks()
- count = 1 # we start with the global stack
+ count = 1 # We start with the global stack
for stack in extruder_stacks:
md = stack.getMetaData()
if "position" in md and int(md["position"]) >= machine_extruder_count:
@@ -646,7 +648,7 @@ class MachineManager(QObject):
new_value = self._active_container_stack.getProperty(key, "value")
extruder_stacks = [stack for stack in ExtruderManager.getInstance().getActiveExtruderStacks()]
- # check in which stack the value has to be replaced
+ # Check in which stack the value has to be replaced
for extruder_stack in extruder_stacks:
if extruder_stack != self._active_container_stack and extruder_stack.getProperty(key, "value") != new_value:
extruder_stack.userChanges.setProperty(key, "value", new_value) # TODO: nested property access, should be improved
@@ -662,7 +664,7 @@ class MachineManager(QObject):
for key in self._active_container_stack.userChanges.getAllKeys():
new_value = self._active_container_stack.getProperty(key, "value")
- # check if the value has to be replaced
+ # Check if the value has to be replaced
extruder_stack.userChanges.setProperty(key, "value", new_value)
@pyqtProperty(str, notify = activeVariantChanged)
@@ -731,7 +733,7 @@ class MachineManager(QObject):
# If the machine that is being removed is the currently active machine, set another machine as the active machine.
activate_new_machine = (self._global_container_stack and self._global_container_stack.getId() == machine_id)
- # activate a new machine before removing a machine because this is safer
+ # Activate a new machine before removing a machine because this is safer
if activate_new_machine:
machine_stacks = CuraContainerRegistry.getInstance().findContainerStacksMetadata(type = "machine")
other_machine_stacks = [s for s in machine_stacks if s["id"] != machine_id]
@@ -909,21 +911,18 @@ class MachineManager(QObject):
# After CURA-4482 this should not be the case anymore, but we still want to support older project files.
global_user_container = self._global_container_stack.userChanges
- # Make sure extruder_stacks exists
- extruder_stacks = [] #type: List[ExtruderStack]
-
- if previous_extruder_count == 1:
- extruder_stacks = ExtruderManager.getInstance().getActiveExtruderStacks()
- global_user_container = self._global_container_stack.userChanges
-
for setting_instance in global_user_container.findInstances():
setting_key = setting_instance.definition.key
settable_per_extruder = self._global_container_stack.getProperty(setting_key, "settable_per_extruder")
if settable_per_extruder:
limit_to_extruder = int(self._global_container_stack.getProperty(setting_key, "limit_to_extruder"))
- extruder_stack = extruder_stacks[max(0, limit_to_extruder)]
- extruder_stack.userChanges.setProperty(setting_key, "value", global_user_container.getProperty(setting_key, "value"))
+ extruder_position = max(0, limit_to_extruder)
+ extruder_stack = self.getExtruder(extruder_position)
+ if extruder_stack:
+ extruder_stack.userChanges.setProperty(setting_key, "value", global_user_container.getProperty(setting_key, "value"))
+ else:
+ Logger.log("e", "Unable to find extruder on position %s", extruder_position)
global_user_container.removeInstance(setting_key)
# Signal that the global stack has changed
@@ -932,10 +931,9 @@ class MachineManager(QObject):
@pyqtSlot(int, result = QObject)
def getExtruder(self, position: int) -> Optional[ExtruderStack]:
- extruder = None
if self._global_container_stack:
- extruder = self._global_container_stack.extruders.get(str(position))
- return extruder
+ return self._global_container_stack.extruders.get(str(position))
+ return None
def updateDefaultExtruder(self) -> None:
if self._global_container_stack is None:
@@ -1001,12 +999,12 @@ class MachineManager(QObject):
if not enabled and position == ExtruderManager.getInstance().activeExtruderIndex:
ExtruderManager.getInstance().setActiveExtruderIndex(int(self._default_extruder_position))
- # ensure that the quality profile is compatible with current combination, or choose a compatible one if available
+ # Ensure that the quality profile is compatible with current combination, or choose a compatible one if available
self._updateQualityWithMaterial()
self.extruderChanged.emit()
- # update material compatibility color
+ # Update material compatibility color
self.activeQualityGroupChanged.emit()
- # update items in SettingExtruder
+ # Update items in SettingExtruder
ExtruderManager.getInstance().extrudersChanged.emit(self._global_container_stack.getId())
# Make sure the front end reflects changes
self.forceUpdateAllSettings()
@@ -1080,7 +1078,6 @@ class MachineManager(QObject):
return result
- #
# Sets all quality and quality_changes containers to empty_quality and empty_quality_changes containers
# for all stacks in the currently active machine.
#
@@ -1139,7 +1136,7 @@ class MachineManager(QObject):
def _setQualityChangesGroup(self, quality_changes_group: "QualityChangesGroup") -> None:
if self._global_container_stack is None:
- return #Can't change that.
+ return # Can't change that.
quality_type = quality_changes_group.quality_type
# A custom quality can be created based on "not supported".
# In that case, do not set quality containers to empty.
@@ -1209,7 +1206,7 @@ class MachineManager(QObject):
self.rootMaterialChanged.emit()
def activeMaterialsCompatible(self) -> bool:
- # check material - variant compatibility
+ # Check material - variant compatibility
if self._global_container_stack is not None:
if Util.parseBool(self._global_container_stack.getMetaDataEntry("has_materials", False)):
for position, extruder in self._global_container_stack.extruders.items():
@@ -1419,7 +1416,7 @@ class MachineManager(QObject):
material_diameter, root_material_id)
self.setMaterial(position, material_node)
- ## global_stack: if you want to provide your own global_stack instead of the current active one
+ ## Global_stack: if you want to provide your own global_stack instead of the current active one
# if you update an active machine, special measures have to be taken.
@pyqtSlot(str, "QVariant")
def setMaterial(self, position: str, container_node, global_stack: Optional["GlobalStack"] = None) -> None:
@@ -1537,3 +1534,22 @@ class MachineManager(QObject):
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
self.updateMaterialWithVariant(None)
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
diff --git a/plugins/CuraEngineBackend/StartSliceJob.py b/plugins/CuraEngineBackend/StartSliceJob.py
index 79b1e5249c..273dc0b6f6 100644
--- a/plugins/CuraEngineBackend/StartSliceJob.py
+++ b/plugins/CuraEngineBackend/StartSliceJob.py
@@ -66,11 +66,19 @@ class GcodeStartEndFormatter(Formatter):
return "{" + key + "}"
key = key_fragments[0]
- try:
- return kwargs[str(extruder_nr)][key]
- except KeyError:
+
+ default_value_str = "{" + key + "}"
+ value = default_value_str
+ # "-1" is global stack, and if the setting value exists in the global stack, use it as the fallback value.
+ if key in kwargs["-1"]:
+ value = kwargs["-1"]
+ if key in kwargs[str(extruder_nr)]:
+ value = kwargs[str(extruder_nr)][key]
+
+ if value == default_value_str:
Logger.log("w", "Unable to replace '%s' placeholder in start/end g-code", key)
- return "{" + key + "}"
+
+ return value
## Job class that builds up the message of scene data to send to CuraEngine.
diff --git a/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py b/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py
index 4c60b95824..9efd3e956a 100644
--- a/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py
+++ b/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py
@@ -93,6 +93,11 @@ class FirmwareUpdateCheckerJob(Job):
current_version = self.getCurrentVersion()
+ # This case indicates that was an error checking the version.
+ # It happens for instance when not connected to internet.
+ if current_version == self.ZERO_VERSION:
+ return
+
# If it is the first time the version is checked, the checked_version is ""
setting_key_str = getSettingsKeyForMachine(machine_id)
checked_version = Version(Application.getInstance().getPreferences().getValue(setting_key_str))
diff --git a/plugins/PostProcessingPlugin/PostProcessingPlugin.py b/plugins/PostProcessingPlugin/PostProcessingPlugin.py
index 11ee610bec..78f9cc0516 100644
--- a/plugins/PostProcessingPlugin/PostProcessingPlugin.py
+++ b/plugins/PostProcessingPlugin/PostProcessingPlugin.py
@@ -55,14 +55,14 @@ class PostProcessingPlugin(QObject, Extension):
def selectedScriptDefinitionId(self) -> Optional[str]:
try:
return self._script_list[self._selected_script_index].getDefinitionId()
- except:
+ except IndexError:
return ""
@pyqtProperty(str, notify=selectedIndexChanged)
def selectedScriptStackId(self) -> Optional[str]:
try:
return self._script_list[self._selected_script_index].getStackId()
- except:
+ except IndexError:
return ""
## Execute all post-processing scripts on the gcode.
diff --git a/plugins/PrepareStage/PrepareMenu.qml b/plugins/PrepareStage/PrepareMenu.qml
index 963908cea6..10b4262f01 100644
--- a/plugins/PrepareStage/PrepareMenu.qml
+++ b/plugins/PrepareStage/PrepareMenu.qml
@@ -1,10 +1,14 @@
+// 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 1.4
+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
{
@@ -24,14 +28,14 @@ Item
Item
{
anchors.horizontalCenter: parent.horizontalCenter
- width: openFileButtonBackground.width + itemRow.width + UM.Theme.getSize("default_margin").width
+ width: openFileButton.width + itemRow.width + UM.Theme.getSize("default_margin").width
height: parent.height
RowLayout
{
id: itemRow
- anchors.left: openFileButtonBackground.right
+ anchors.left: openFileButton.right
anchors.leftMargin: UM.Theme.getSize("default_margin").width
width: Math.round(0.9 * prepareMenu.width)
@@ -41,7 +45,7 @@ Item
Cura.MachineSelector
{
id: machineSelection
- z: openFileButtonBackground.z - 1 //Ensure that the tooltip of the open file button stays above the item row.
+ z: openFileButton.z - 1 //Ensure that the tooltip of the open file button stays above the item row.
headerCornerSide: Cura.RoundedRectangle.Direction.Left
Layout.minimumWidth: UM.Theme.getSize("machine_selector_widget").width
Layout.maximumWidth: UM.Theme.getSize("machine_selector_widget").width
@@ -83,24 +87,53 @@ Item
}
}
-
- Rectangle
+ Button
{
- id: openFileButtonBackground
+ id: openFileButton
height: UM.Theme.getSize("stage_menu").height
width: UM.Theme.getSize("stage_menu").height
+ onClicked: Cura.Actions.open.trigger()
+ hoverEnabled: true
- radius: UM.Theme.getSize("default_radius").width
- color: UM.Theme.getColor("toolbar_background")
- Button
+ contentItem: Item
{
- id: openFileButton
- text: catalog.i18nc("@action:button", "Open File")
- iconSource: UM.Theme.getIcon("load")
- style: UM.Theme.styles.toolbar_button
- tooltip: ""
- action: Cura.Actions.open
- anchors.centerIn: parent
+ 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.width: width
+ 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
}
}
}
diff --git a/plugins/PreviewStage/PreviewMenu.qml b/plugins/PreviewStage/PreviewMenu.qml
index d660db549b..a1f59cd4ca 100644
--- a/plugins/PreviewStage/PreviewMenu.qml
+++ b/plugins/PreviewStage/PreviewMenu.qml
@@ -29,80 +29,12 @@ Item
anchors.centerIn: parent
height: parent.height
- Cura.ExpandableComponent
+ Cura.ViewsSelector
{
- id: viewSelector
- iconSource: expanded ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left")
+ id: viewsSelector
height: parent.height
width: UM.Theme.getSize("views_selector").width
headerCornerSide: Cura.RoundedRectangle.Direction.Left
-
- property var viewModel: UM.ViewModel { }
-
- property var activeView:
- {
- for (var i = 0; i < viewModel.rowCount(); i++)
- {
- if (viewModel.items[i].active)
- {
- return viewModel.items[i]
- }
- }
- return null
- }
-
- Component.onCompleted:
- {
- // Nothing was active, so just return the first one (the list is sorted by priority, so the most
- // important one should be returned)
- if (activeView == null)
- {
- UM.Controller.setActiveView(viewModel.getItem(0).id)
- }
- }
-
- headerItem: Label
- {
- text: viewSelector.activeView ? viewSelector.activeView.name : ""
- verticalAlignment: Text.AlignVCenter
- height: parent.height
- elide: Text.ElideRight
- font: UM.Theme.getFont("default")
- color: UM.Theme.getColor("text")
- renderType: Text.NativeRendering
- }
-
- popupItem: Column
- {
- id: viewSelectorPopup
- width: viewSelector.width - 2 * UM.Theme.getSize("default_margin").width
-
- // For some reason the height/width of the column gets set to 0 if this is not set...
- Component.onCompleted:
- {
- height = implicitHeight
- width = viewSelector.width - 2 * UM.Theme.getSize("default_margin").width
- }
-
- Repeater
- {
- id: viewsList
- model: viewSelector.viewModel
- RoundButton
- {
- text: name
- radius: UM.Theme.getSize("default_radius").width
- checkable: true
- checked: viewSelector.activeView != null ? viewSelector.activeView.id == id : false
- onClicked:
- {
- viewSelector.togglePopup()
- UM.Controller.setActiveView(id)
- }
- }
- }
-
- }
}
// Separator line
diff --git a/plugins/SimulationView/SimulationViewMainComponent.qml b/plugins/SimulationView/SimulationViewMainComponent.qml
index 4e038a1cf1..16b9aeaae6 100644
--- a/plugins/SimulationView/SimulationViewMainComponent.qml
+++ b/plugins/SimulationView/SimulationViewMainComponent.qml
@@ -6,7 +6,7 @@ import QtQuick.Controls 1.2
import QtQuick.Layouts 1.1
import QtQuick.Controls.Styles 1.1
-import UM 1.0 as UM
+import UM 1.4 as UM
import Cura 1.0 as Cura
Item
@@ -55,11 +55,16 @@ Item
}
- Button
+ UM.SimpleButton
{
id: playButton
iconSource: !is_simulation_playing ? "./resources/simulation_resume.svg": "./resources/simulation_pause.svg"
- style: UM.Theme.styles.small_tool_button
+ width: UM.Theme.getSize("small_button").width
+ height: UM.Theme.getSize("small_button").height
+ hoverBackgroundColor: UM.Theme.getColor("small_button_hover")
+ hoverColor: UM.Theme.getColor("small_button_text_hover")
+ color: UM.Theme.getColor("small_button_text")
+ iconMargin: 0.5 * UM.Theme.getSize("wide_lining").width
visible: !UM.SimulationView.compatibilityMode
Connections
diff --git a/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml b/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml
index 437a2ef351..0c04dc2bab 100644
--- a/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml
+++ b/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml
@@ -37,7 +37,7 @@ Item
leftMargin: UM.Theme.getSize("wide_margin").width
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
{
anchors.fill: parent
diff --git a/plugins/UM3NetworkPrinting/src/Models.py b/plugins/UM3NetworkPrinting/src/Models.py
new file mode 100644
index 0000000000..2bcac70766
--- /dev/null
+++ b/plugins/UM3NetworkPrinting/src/Models.py
@@ -0,0 +1,43 @@
+# Copyright (c) 2018 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
+
+## Base model that maps kwargs to instance attributes.
+class BaseModel:
+ def __init__(self, **kwargs) -> None:
+ self.__dict__.update(kwargs)
+ self.validate()
+
+ def validate(self) -> None:
+ pass
+
+
+## Class representing a material that was fetched from the cluster API.
+class ClusterMaterial(BaseModel):
+ def __init__(self, guid: str, version: int, **kwargs) -> None:
+ self.guid = guid # type: str
+ self.version = version # type: int
+ super().__init__(**kwargs)
+
+ def validate(self) -> None:
+ if not self.guid:
+ raise ValueError("guid is required on ClusterMaterial")
+ if not self.version:
+ raise ValueError("version is required on ClusterMaterial")
+
+
+## Class representing a local material that was fetched from the container registry.
+class LocalMaterial(BaseModel):
+ def __init__(self, GUID: str, id: str, version: int, **kwargs) -> None:
+ self.GUID = GUID # type: str
+ self.id = id # type: str
+ self.version = version # type: int
+ super().__init__(**kwargs)
+
+ def validate(self) -> None:
+ if not self.GUID:
+ raise ValueError("guid is required on LocalMaterial")
+ if not self.version:
+ raise ValueError("version is required on LocalMaterial")
+ if not self.id:
+ raise ValueError("id is required on LocalMaterial")
diff --git a/plugins/UM3NetworkPrinting/src/SendMaterialJob.py b/plugins/UM3NetworkPrinting/src/SendMaterialJob.py
index 8491e79c29..f536fad49a 100644
--- a/plugins/UM3NetworkPrinting/src/SendMaterialJob.py
+++ b/plugins/UM3NetworkPrinting/src/SendMaterialJob.py
@@ -1,99 +1,197 @@
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
+import json
+import os
+import urllib.parse
+from typing import Dict, TYPE_CHECKING, Set
-import json #To understand the list of materials from the printer reply.
-import os #To walk over material files.
-import os.path #To filter on material files.
-from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest #To listen to the reply from the printer.
-from typing import Any, Dict, Set, TYPE_CHECKING
-import urllib.parse #For getting material IDs from their file names.
+from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest
-from UM.Job import Job #The interface we're implementing.
+from UM.Application import Application
+from UM.Job import Job
from UM.Logger import Logger
-from UM.MimeTypeDatabase import MimeTypeDatabase #To strip the extensions of the material profile files.
+from UM.MimeTypeDatabase import MimeTypeDatabase
from UM.Resources import Resources
-from UM.Settings.ContainerRegistry import ContainerRegistry #To find the GUIDs of materials.
-
-from cura.CuraApplication import CuraApplication #For the resource types.
+from cura.CuraApplication import CuraApplication
+# Absolute imports don't work in plugins
+from .Models import ClusterMaterial, LocalMaterial
if TYPE_CHECKING:
from .ClusterUM3OutputDevice import ClusterUM3OutputDevice
+
## Asynchronous job to send material profiles to the printer.
#
# This way it won't freeze up the interface while sending those materials.
class SendMaterialJob(Job):
+
def __init__(self, device: "ClusterUM3OutputDevice") -> None:
super().__init__()
- self.device = device #type: ClusterUM3OutputDevice
+ self.device = device # type: ClusterUM3OutputDevice
+ ## Send the request to the printer and register a callback
def run(self) -> None:
- self.device.get("materials/", on_finished = self.sendMissingMaterials)
+ self.device.get("materials/", on_finished = self._onGetRemoteMaterials)
- def sendMissingMaterials(self, reply: QNetworkReply) -> None:
- if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) != 200: #Got an error from the HTTP request.
- Logger.log("e", "Couldn't request current material storage on printer. Not syncing materials.")
+ ## Process the materials reply from the printer.
+ #
+ # \param reply The reply from the printer, a json file.
+ def _onGetRemoteMaterials(self, reply: QNetworkReply) -> None:
+
+ # Got an error from the HTTP request. If we did not receive a 200 something happened.
+ if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) != 200:
+ Logger.log("e", "Error fetching materials from printer: %s", reply.errorString())
return
- remote_materials_list = reply.readAll().data().decode("utf-8")
- try:
- remote_materials_list = json.loads(remote_materials_list)
- except json.JSONDecodeError:
- Logger.log("e", "Request material storage on printer: I didn't understand the printer's answer.")
- return
- try:
- remote_materials_by_guid = {material["guid"]: material for material in remote_materials_list} #Index by GUID.
- except KeyError:
- Logger.log("e", "Request material storage on printer: Printer's answer was missing GUIDs.")
+ # Collect materials from the printer's reply and send the missing ones if needed.
+ remote_materials_by_guid = self._parseReply(reply)
+ if remote_materials_by_guid:
+ self._sendMissingMaterials(remote_materials_by_guid)
+
+ ## Determine which materials should be updated and send them to the printer.
+ #
+ # \param remote_materials_by_guid The remote materials by GUID.
+ def _sendMissingMaterials(self, remote_materials_by_guid: Dict[str, ClusterMaterial]) -> None:
+
+ # Collect local materials
+ local_materials_by_guid = self._getLocalMaterials()
+ if len(local_materials_by_guid) == 0:
+ Logger.log("d", "There are no local materials to synchronize with the printer.")
return
- container_registry = ContainerRegistry.getInstance()
- local_materials_list = filter(lambda material: ("GUID" in material and "version" in material and "id" in material), container_registry.findContainersMetadata(type = "material"))
- local_materials_by_guid = {material["GUID"]: material for material in local_materials_list if material["id"] == material["base_file"]}
- for material in local_materials_list: #For each GUID get the material with the highest version number.
- try:
- if int(material["version"]) > local_materials_by_guid[material["GUID"]]["version"]:
- local_materials_by_guid[material["GUID"]] = material
- except ValueError:
- Logger.log("e", "Material {material_id} has invalid version number {number}.".format(material_id = material["id"], number = material["version"]))
- continue
+ # Find out what materials are new or updated and must be sent to the printer
+ material_ids_to_send = self._determineMaterialsToSend(local_materials_by_guid, remote_materials_by_guid)
+ if len(material_ids_to_send) == 0:
+ Logger.log("d", "There are no remote materials to update.")
+ return
- materials_to_send = set() #type: Set[Dict[str, Any]]
- for guid, material in local_materials_by_guid.items():
- if guid not in remote_materials_by_guid:
- materials_to_send.add(material["id"])
- continue
- try:
- if int(material["version"]) > remote_materials_by_guid[guid]["version"]:
- materials_to_send.add(material["id"])
- continue
- except KeyError:
- Logger.log("e", "Current material storage on printer was an invalid reply (missing version).")
- return
+ # Send materials to the printer
+ self._sendMaterials(material_ids_to_send)
- for file_path in Resources.getAllResourcesOfType(CuraApplication.ResourceTypes.MaterialInstanceContainer):
+ ## From the local and remote materials, determine which ones should be synchronized.
+ #
+ # Makes a Set of id's containing only the id's of the materials that are not on the printer yet or the ones that
+ # are newer in Cura.
+ #
+ # \param local_materials The local materials by GUID.
+ # \param remote_materials The remote materials by GUID.
+ @staticmethod
+ def _determineMaterialsToSend(local_materials: Dict[str, LocalMaterial],
+ remote_materials: Dict[str, ClusterMaterial]) -> Set[str]:
+ return {
+ material.id
+ for guid, material in local_materials.items()
+ if guid not in remote_materials or material.version > remote_materials[guid].version
+ }
+
+ ## Send the materials to the printer.
+ #
+ # The given materials will be loaded from disk en sent to to printer.
+ # The given id's will be matched with filenames of the locally stored materials.
+ #
+ # \param materials_to_send A set with id's of materials that must be sent.
+ def _sendMaterials(self, materials_to_send: Set[str]) -> None:
+ file_paths = Resources.getAllResourcesOfType(CuraApplication.ResourceTypes.MaterialInstanceContainer)
+
+ # Find all local material files and send them if needed.
+ for file_path in file_paths:
try:
mime_type = MimeTypeDatabase.getMimeTypeForFile(file_path)
except MimeTypeDatabase.MimeTypeNotFoundError:
- continue #Not the sort of file we'd like to send then.
- _, file_name = os.path.split(file_path)
- material_id = urllib.parse.unquote_plus(mime_type.stripExtension(file_name))
- if material_id not in materials_to_send:
continue
- parts = []
- with open(file_path, "rb") as f:
- parts.append(self.device._createFormPart("name=\"file\"; filename=\"{file_name}\"".format(file_name = file_name), f.read()))
- signature_file_path = file_path + ".sig"
- if os.path.exists(signature_file_path):
- _, signature_file_name = os.path.split(signature_file_path)
- with open(signature_file_path, "rb") as f:
- parts.append(self.device._createFormPart("name=\"signature_file\"; filename=\"{file_name}\"".format(file_name = signature_file_name), f.read()))
+ file_name = os.path.basename(file_path)
+ material_id = urllib.parse.unquote_plus(mime_type.stripExtension(file_name))
+ if material_id not in materials_to_send:
+ # If the material does not have to be sent we skip it.
+ continue
- Logger.log("d", "Syncing material {material_id} with cluster.".format(material_id = material_id))
- self.device.postFormWithParts(target = "materials/", parts = parts, on_finished = self.sendingFinished)
+ self._sendMaterialFile(file_path, file_name, material_id)
- def sendingFinished(self, reply: QNetworkReply):
+ ## Send a single material file to the printer.
+ #
+ # Also add the material signature file if that is available.
+ #
+ # \param file_path The path of the material file.
+ # \param file_name The name of the material file.
+ # \param material_id The ID of the material in the file.
+ def _sendMaterialFile(self, file_path: str, file_name: str, material_id: str) -> None:
+
+ parts = []
+
+ # Add the material file.
+ with open(file_path, "rb") as f:
+ parts.append(self.device.createFormPart("name=\"file\"; filename=\"{file_name}\""
+ .format(file_name = file_name), f.read()))
+
+ # Add the material signature file if needed.
+ signature_file_path = "{}.sig".format(file_path)
+ if os.path.exists(signature_file_path):
+ signature_file_name = os.path.basename(signature_file_path)
+ with open(signature_file_path, "rb") as f:
+ parts.append(self.device.createFormPart("name=\"signature_file\"; filename=\"{file_name}\""
+ .format(file_name = signature_file_name), f.read()))
+
+ Logger.log("d", "Syncing material {material_id} with cluster.".format(material_id = material_id))
+ self.device.postFormWithParts(target = "materials/", parts = parts, on_finished = self.sendingFinished)
+
+ ## Check a reply from an upload to the printer and log an error when the call failed
+ @staticmethod
+ def sendingFinished(reply: QNetworkReply) -> None:
if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) != 200:
- Logger.log("e", "Received error code from printer when syncing material: {code}".format(code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute)))
- Logger.log("e", reply.readAll().data().decode("utf-8"))
\ No newline at end of file
+ Logger.log("e", "Received error code from printer when syncing material: {code}, {text}".format(
+ code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute),
+ text = reply.errorString()
+ ))
+
+ ## Parse the reply from the printer
+ #
+ # Parses the reply to a "/materials" request to the printer
+ #
+ # \return a dictionary of ClusterMaterial objects by GUID
+ # \throw KeyError Raised when on of the materials does not include a valid guid
+ @classmethod
+ def _parseReply(cls, reply: QNetworkReply) -> Dict[str, ClusterMaterial]:
+ try:
+ remote_materials = json.loads(reply.readAll().data().decode("utf-8"))
+ return {material["guid"]: ClusterMaterial(**material) for material in remote_materials}
+ except UnicodeDecodeError:
+ Logger.log("e", "Request material storage on printer: I didn't understand the printer's answer.")
+ except json.JSONDecodeError:
+ Logger.log("e", "Request material storage on printer: I didn't understand the printer's answer.")
+ except ValueError:
+ Logger.log("e", "Request material storage on printer: Printer's answer had an incorrect value.")
+ except TypeError:
+ Logger.log("e", "Request material storage on printer: Printer's answer was missing a required value.")
+
+ ## Retrieves a list of local materials
+ #
+ # Only the new newest version of the local materials is returned
+ #
+ # \return a dictionary of LocalMaterial objects by GUID
+ def _getLocalMaterials(self) -> Dict[str, LocalMaterial]:
+ result = {} # type: Dict[str, LocalMaterial]
+ container_registry = Application.getInstance().getContainerRegistry()
+ material_containers = container_registry.findContainersMetadata(type = "material")
+
+ # Find the latest version of all material containers in the registry.
+ for material in material_containers:
+ try:
+ # material version must be an int
+ material["version"] = int(material["version"])
+
+ # Create a new local material
+ local_material = LocalMaterial(**material)
+
+ if local_material.GUID not in result or \
+ local_material.version > result.get(local_material.GUID).version:
+ result[local_material.GUID] = local_material
+
+ except KeyError:
+ Logger.logException("w", "Local material {} has missing values.".format(material["id"]))
+ except ValueError:
+ Logger.logException("w", "Local material {} has invalid values.".format(material["id"]))
+ except TypeError:
+ Logger.logException("w", "Local material {} has invalid values.".format(material["id"]))
+
+ return result
diff --git a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py
index 9c070f2de2..daea696cd1 100644
--- a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py
+++ b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py
@@ -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.
from UM.OutputDevice.OutputDevicePlugin import OutputDevicePlugin
@@ -325,13 +325,12 @@ class UM3OutputDevicePlugin(OutputDevicePlugin):
## Handler for zeroConf detection.
# Return True or False indicating if the process succeeded.
- # Note that this function can take over 3 seconds to complete. Be carefull calling it from the main thread.
+ # Note that this function can take over 3 seconds to complete. Be careful
+ # calling it from the main thread.
def _onServiceChanged(self, zero_conf, service_type, name, state_change):
if state_change == ServiceStateChange.Added:
- Logger.log("d", "Bonjour service added: %s" % name)
-
# First try getting info from zero-conf cache
- info = ServiceInfo(service_type, name, properties={})
+ info = ServiceInfo(service_type, name, properties = {})
for record in zero_conf.cache.entries_with_name(name.lower()):
info.update_record(zero_conf, time(), record)
diff --git a/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py b/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py
new file mode 100644
index 0000000000..b669eb192a
--- /dev/null
+++ b/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py
@@ -0,0 +1,190 @@
+# Copyright (c) 2018 Ultimaker B.V.
+# Copyright (c) 2018 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+import io
+import json
+from unittest import TestCase, mock
+from unittest.mock import patch, call
+
+from PyQt5.QtCore import QByteArray
+
+from UM.MimeTypeDatabase import MimeType
+from UM.Application import Application
+from plugins.UM3NetworkPrinting.src.SendMaterialJob import SendMaterialJob
+
+
+@patch("builtins.open", lambda _, __: io.StringIO(""))
+@patch("UM.MimeTypeDatabase.MimeTypeDatabase.getMimeTypeForFile",
+ lambda _: MimeType(name = "application/x-ultimaker-material-profile", comment = "Ultimaker Material Profile",
+ suffixes = ["xml.fdm_material"]))
+@patch("UM.Resources.Resources.getAllResourcesOfType", lambda _: ["/materials/generic_pla_white.xml.fdm_material"])
+@patch("plugins.UM3NetworkPrinting.src.ClusterUM3OutputDevice")
+@patch("PyQt5.QtNetwork.QNetworkReply")
+class TestSendMaterialJob(TestCase):
+ _LOCAL_MATERIAL_WHITE = {"type": "material", "status": "unknown", "id": "generic_pla_white",
+ "base_file": "generic_pla_white", "setting_version": "5", "name": "White PLA",
+ "brand": "Generic", "material": "PLA", "color_name": "White",
+ "GUID": "badb0ee7-87c8-4f3f-9398-938587b67dce", "version": "1", "color_code": "#ffffff",
+ "description": "Test PLA White", "adhesion_info": "Use glue.", "approximate_diameter": "3",
+ "properties": {"density": "1.00", "diameter": "2.85", "weight": "750"},
+ "definition": "fdmprinter", "compatible": True}
+
+ _LOCAL_MATERIAL_BLACK = {"type": "material", "status": "unknown", "id": "generic_pla_black",
+ "base_file": "generic_pla_black", "setting_version": "5", "name": "Yellow CPE",
+ "brand": "Ultimaker", "material": "CPE", "color_name": "Black",
+ "GUID": "5fbb362a-41f9-4818-bb43-15ea6df34aa4", "version": "1", "color_code": "#000000",
+ "description": "Test PLA Black", "adhesion_info": "Use glue.", "approximate_diameter": "3",
+ "properties": {"density": "1.01", "diameter": "2.85", "weight": "750"},
+ "definition": "fdmprinter", "compatible": True}
+
+ _REMOTE_MATERIAL_WHITE = {
+ "guid": "badb0ee7-87c8-4f3f-9398-938587b67dce",
+ "material": "PLA",
+ "brand": "Generic",
+ "version": 1,
+ "color": "White",
+ "density": 1.00
+ }
+
+ _REMOTE_MATERIAL_BLACK = {
+ "guid": "5fbb362a-41f9-4818-bb43-15ea6df34aa4",
+ "material": "PLA",
+ "brand": "Generic",
+ "version": 2,
+ "color": "Black",
+ "density": 1.00
+ }
+
+ def test_run(self, device_mock, reply_mock):
+ job = SendMaterialJob(device_mock)
+ job.run()
+
+ # We expect the materials endpoint to be called when the job runs.
+ device_mock.get.assert_called_with("materials/", on_finished = job._onGetRemoteMaterials)
+
+ def test__onGetRemoteMaterials_withFailedRequest(self, reply_mock, device_mock):
+ reply_mock.attribute.return_value = 404
+ job = SendMaterialJob(device_mock)
+ job._onGetRemoteMaterials(reply_mock)
+
+ # We expect the device not to be called for any follow up.
+ self.assertEqual(0, device_mock.createFormPart.call_count)
+
+ def test__onGetRemoteMaterials_withWrongEncoding(self, reply_mock, device_mock):
+ reply_mock.attribute.return_value = 200
+ reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTE_MATERIAL_WHITE]).encode("cp500"))
+ job = SendMaterialJob(device_mock)
+ job._onGetRemoteMaterials(reply_mock)
+
+ # Given that the parsing fails we do no expect the device to be called for any follow up.
+ self.assertEqual(0, device_mock.createFormPart.call_count)
+
+ def test__onGetRemoteMaterials_withBadJsonAnswer(self, reply_mock, device_mock):
+ reply_mock.attribute.return_value = 200
+ reply_mock.readAll.return_value = QByteArray(b"Six sick hicks nick six slick bricks with picks and sticks.")
+ job = SendMaterialJob(device_mock)
+ job._onGetRemoteMaterials(reply_mock)
+
+ # Given that the parsing fails we do no expect the device to be called for any follow up.
+ self.assertEqual(0, device_mock.createFormPart.call_count)
+
+ def test__onGetRemoteMaterials_withMissingGuidInRemoteMaterial(self, reply_mock, device_mock):
+ reply_mock.attribute.return_value = 200
+ remote_material_without_guid = self._REMOTE_MATERIAL_WHITE.copy()
+ del remote_material_without_guid["guid"]
+ reply_mock.readAll.return_value = QByteArray(json.dumps([remote_material_without_guid]).encode("ascii"))
+ job = SendMaterialJob(device_mock)
+ job._onGetRemoteMaterials(reply_mock)
+
+ # Given that parsing fails we do not expect the device to be called for any follow up.
+ self.assertEqual(0, device_mock.createFormPart.call_count)
+
+ @patch("cura.Settings.CuraContainerRegistry")
+ @patch("UM.Application")
+ def test__onGetRemoteMaterials_withInvalidVersionInLocalMaterial(self, application_mock, container_registry_mock,
+ reply_mock, device_mock):
+ reply_mock.attribute.return_value = 200
+ reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTE_MATERIAL_WHITE]).encode("ascii"))
+
+ localMaterialWhiteWithInvalidVersion = self._LOCAL_MATERIAL_WHITE.copy()
+ localMaterialWhiteWithInvalidVersion["version"] = "one"
+ container_registry_mock.findContainersMetadata.return_value = [localMaterialWhiteWithInvalidVersion]
+
+ application_mock.getContainerRegistry.return_value = container_registry_mock
+
+ with mock.patch.object(Application, "getInstance", new = lambda: application_mock):
+ job = SendMaterialJob(device_mock)
+ job._onGetRemoteMaterials(reply_mock)
+
+ self.assertEqual(0, device_mock.createFormPart.call_count)
+
+ @patch("cura.Settings.CuraContainerRegistry")
+ @patch("UM.Application")
+ def test__onGetRemoteMaterials_withNoUpdate(self, application_mock, container_registry_mock, reply_mock,
+ device_mock):
+ application_mock.getContainerRegistry.return_value = container_registry_mock
+
+ device_mock.createFormPart.return_value = "_xXx_"
+
+ container_registry_mock.findContainersMetadata.return_value = [self._LOCAL_MATERIAL_WHITE]
+
+ reply_mock.attribute.return_value = 200
+ reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTE_MATERIAL_WHITE]).encode("ascii"))
+
+ with mock.patch.object(Application, "getInstance", new = lambda: application_mock):
+ job = SendMaterialJob(device_mock)
+ job._onGetRemoteMaterials(reply_mock)
+
+ self.assertEqual(0, device_mock.createFormPart.call_count)
+ self.assertEqual(0, device_mock.postFormWithParts.call_count)
+
+ @patch("cura.Settings.CuraContainerRegistry")
+ @patch("UM.Application")
+ def test__onGetRemoteMaterials_withUpdatedMaterial(self, application_mock, container_registry_mock, reply_mock,
+ device_mock):
+ application_mock.getContainerRegistry.return_value = container_registry_mock
+
+ device_mock.createFormPart.return_value = "_xXx_"
+
+ localMaterialWhiteWithHigherVersion = self._LOCAL_MATERIAL_WHITE.copy()
+ localMaterialWhiteWithHigherVersion["version"] = "2"
+ container_registry_mock.findContainersMetadata.return_value = [localMaterialWhiteWithHigherVersion]
+
+ reply_mock.attribute.return_value = 200
+ reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTE_MATERIAL_WHITE]).encode("ascii"))
+
+ with mock.patch.object(Application, "getInstance", new = lambda: application_mock):
+ job = SendMaterialJob(device_mock)
+ job._onGetRemoteMaterials(reply_mock)
+
+ self.assertEqual(1, device_mock.createFormPart.call_count)
+ self.assertEqual(1, device_mock.postFormWithParts.call_count)
+ self.assertEquals(
+ [call.createFormPart("name=\"file\"; filename=\"generic_pla_white.xml.fdm_material\"", ""),
+ call.postFormWithParts(target = "materials/", parts = ["_xXx_"], on_finished = job.sendingFinished)],
+ device_mock.method_calls)
+
+ @patch("cura.Settings.CuraContainerRegistry")
+ @patch("UM.Application")
+ def test__onGetRemoteMaterials_withNewMaterial(self, application_mock, container_registry_mock, reply_mock,
+ device_mock):
+ application_mock.getContainerRegistry.return_value = container_registry_mock
+
+ device_mock.createFormPart.return_value = "_xXx_"
+
+ container_registry_mock.findContainersMetadata.return_value = [self._LOCAL_MATERIAL_WHITE,
+ self._LOCAL_MATERIAL_BLACK]
+
+ reply_mock.attribute.return_value = 200
+ reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTE_MATERIAL_BLACK]).encode("ascii"))
+
+ with mock.patch.object(Application, "getInstance", new = lambda: application_mock):
+ job = SendMaterialJob(device_mock)
+ job._onGetRemoteMaterials(reply_mock)
+
+ self.assertEqual(1, device_mock.createFormPart.call_count)
+ self.assertEqual(1, device_mock.postFormWithParts.call_count)
+ self.assertEquals(
+ [call.createFormPart("name=\"file\"; filename=\"generic_pla_white.xml.fdm_material\"", ""),
+ call.postFormWithParts(target = "materials/", parts = ["_xXx_"], on_finished = job.sendingFinished)],
+ device_mock.method_calls)
diff --git a/plugins/USBPrinting/AutoDetectBaudJob.py b/plugins/USBPrinting/AutoDetectBaudJob.py
index 8b37c4b29d..6f1af6727a 100644
--- a/plugins/USBPrinting/AutoDetectBaudJob.py
+++ b/plugins/USBPrinting/AutoDetectBaudJob.py
@@ -3,6 +3,7 @@
from UM.Job import Job
from UM.Logger import Logger
+from plugins.USBPrinting.avr_isp import ispBase
from .avr_isp.stk500v2 import Stk500v2
@@ -14,12 +15,12 @@ from serial import Serial, SerialException
# It tries a pre-set list of baud rates. All these baud rates are validated by requesting the temperature a few times
# and checking if the results make sense. If getResult() is not None, it was able to find a correct baud rate.
class AutoDetectBaudJob(Job):
- def __init__(self, serial_port):
+ def __init__(self, serial_port: int) -> None:
super().__init__()
self._serial_port = serial_port
self._all_baud_rates = [115200, 250000, 230400, 57600, 38400, 19200, 9600]
- def run(self):
+ def run(self) -> None:
Logger.log("d", "Auto detect baud rate started.")
wait_response_timeouts = [3, 15, 30]
wait_bootloader_times = [1.5, 5, 15]
@@ -32,7 +33,7 @@ class AutoDetectBaudJob(Job):
try:
programmer.connect(self._serial_port)
serial = programmer.leaveISP()
- except:
+ except ispBase.IspError:
programmer.close()
for retry in range(tries):
@@ -58,7 +59,7 @@ class AutoDetectBaudJob(Job):
# We already have a serial connection, just change the baud rate.
try:
serial.baudrate = baud_rate
- except:
+ except ValueError:
continue
sleep(wait_bootloader) # Ensure that we are not talking to the boot loader. 1.5 seconds seems to be the magic number
successful_responses = 0
@@ -81,5 +82,5 @@ class AutoDetectBaudJob(Job):
return
serial.write(b"M105\n")
- sleep(15) # Give the printer some time to init and try again.
+ sleep(15) # Give the printer some time to init and try again.
self.setResult(None) # Unable to detect the correct baudrate.
diff --git a/resources/definitions/alfawise_u20.def.json b/resources/definitions/alfawise_u20.def.json
new file mode 100644
index 0000000000..87726fec3d
--- /dev/null
+++ b/resources/definitions/alfawise_u20.def.json
@@ -0,0 +1,96 @@
+{
+ "name": "Alfawise U20",
+ "version": 2,
+ "inherits": "fdmprinter",
+ "metadata": {
+ "visible": true,
+ "author": "Samuel Pinches",
+ "manufacturer": "Alfawise",
+ "file_formats": "text/x-gcode",
+ "preferred_quality_type": "fine",
+ "machine_extruder_trains":
+ {
+ "0": "alfawise_u20_extruder_0"
+ }
+ },
+ "overrides": {
+ "machine_name": {
+ "default_value": "Alfawise U20"
+ },
+ "machine_start_gcode": {
+ "default_value": "; -- START GCODE --\nG21 ;set units to millimetres\nG90 ;set to absolute positioning\nM106 S0 ;set fan speed to zero (turned off)\nG28 ;home all axis\nG92 E0 ;zero the extruded length\nG1 Z1 F1000 ;move up slightly\nG1 X60.0 Z0 E9.0 F1000.0;intro line\nG1 X100.0 E21.5 F1000.0 ;continue line\nG92 E0 ;zero the extruded length again\n; -- end of START GCODE --"
+ },
+ "machine_end_gcode": {
+ "default_value": "; -- END GCODE --\nM104 S0 ;turn off nozzle heater\nM140 S0 ;turn off bed heater\nG91 ;set to relative positioning\nG1 E-10 F300 ;retract the filament slightly\nG90 ;set to absolute positioning\nG28 X0 ;move to the X-axis origin (Home)\nG0 Y280 F600 ;bring the bed to the front for easy print removal\nM84 ;turn off stepper motors\n; -- end of END GCODE --"
+ },
+ "machine_width": {
+ "default_value": 300
+ },
+ "machine_height": {
+ "default_value": 400
+ },
+ "machine_depth": {
+ "default_value": 300
+ },
+ "machine_heated_bed": {
+ "default_value": true
+ },
+ "machine_center_is_zero": {
+ "default_value": false
+ },
+ "gantry_height": {
+ "default_value": 10
+ },
+ "machine_gcode_flavor": {
+ "default_value": "RepRap (Marlin/Sprinter)"
+ },
+ "material_diameter": {
+ "default_value": 1.75
+ },
+ "material_print_temperature": {
+ "default_value": 210
+ },
+ "material_bed_temperature": {
+ "default_value": 50
+ },
+ "layer_height": {
+ "default_value": 0.15
+ },
+ "layer_height_0": {
+ "default_value": 0.2
+ },
+ "wall_thickness": {
+ "default_value": 1.2
+ },
+ "speed_print": {
+ "default_value": 40
+ },
+ "speed_infill": {
+ "default_value": 40
+ },
+ "speed_wall": {
+ "default_value": 35
+ },
+ "speed_topbottom": {
+ "default_value": 35
+ },
+ "speed_travel": {
+ "default_value": 120
+ },
+ "speed_layer_0": {
+ "default_value": 20
+ },
+ "support_enable": {
+ "default_value": true
+ },
+ "retraction_enable": {
+ "default_value": true
+ },
+ "retraction_amount": {
+ "default_value": 5
+ },
+ "retraction_speed": {
+ "default_value": 45
+ }
+ }
+}
diff --git a/resources/definitions/bibo2_dual.def.json b/resources/definitions/bibo2_dual.def.json
new file mode 100644
index 0000000000..2036290ebd
--- /dev/null
+++ b/resources/definitions/bibo2_dual.def.json
@@ -0,0 +1,92 @@
+{
+ "id": "BIBO2 dual",
+ "version": 2,
+ "name": "BIBO2 dual",
+ "inherits": "fdmprinter",
+ "metadata": {
+ "visible": true,
+ "author": "na",
+ "manufacturer": "BIBO",
+ "category": "Other",
+ "file_formats": "text/x-gcode",
+ "has_materials": true,
+ "machine_extruder_trains": {
+ "0": "bibo2_dual_extruder_0",
+ "1": "bibo2_dual_extruder_1"
+ },
+ "first_start_actions": [
+ "MachineSettingsAction"
+ ]
+ },
+ "overrides": {
+ "machine_name": {
+ "default_value": "BIBO2 dual"
+ },
+ "machine_width": {
+ "default_value": 214
+ },
+ "machine_height": {
+ "default_value": 160
+ },
+ "machine_depth": {
+ "default_value": 186
+ },
+ "machine_center_is_zero": {
+ "default_value": true
+ },
+ "machine_heated_bed": {
+ "default_value": true
+ },
+ "machine_nozzle_heat_up_speed": {
+ "default_value": 2
+ },
+ "machine_nozzle_cool_down_speed": {
+ "default_value": 2
+ },
+ "machine_head_with_fans_polygon": {
+ "default_value": [
+ [
+ -68.18,
+ 64.63
+ ],
+ [
+ -68.18,
+ -47.38
+ ],
+ [
+ 35.18,
+ 64.63
+ ],
+ [
+ 35.18,
+ -47.38
+ ]
+ ]
+ },
+ "gantry_height": {
+ "default_value": 12
+ },
+ "machine_use_extruder_offset_to_offset_coords": {
+ "default_value": true
+ },
+ "machine_gcode_flavor": {
+ "default_value": "RepRap (Marlin/Sprinter)"
+ },
+ "machine_start_gcode": {
+ "default_value": "G21 ;metric values\nG90 ;absolute positioning\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nG1 Z2.0 F400 ;move the platform down 15mm\nT0\nG92 E0\nG28\nG1 Y0 F1200 E0\nG92 E0\nM117 BIBO Printing..."
+ },
+ "machine_end_gcode": {
+ "default_value": ";End GCode\nM104 T0 S0 ;extruder heater off\nM104 T1 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG91\nG1 Z1 F100 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-2 X-20 Y-20 F300 ;move Z up a bit and retract filament even more\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning"
+ },
+ "machine_extruder_count": {
+ "default_value": 2
+ },
+ "prime_tower_position_x": {
+ "default_value": 50
+ },
+ "prime_tower_position_y": {
+ "default_value": 50
+ }
+ }
+}
+
diff --git a/resources/definitions/cocoon_create_modelmaker.def.json b/resources/definitions/cocoon_create_modelmaker.def.json
new file mode 100644
index 0000000000..204d5b9492
--- /dev/null
+++ b/resources/definitions/cocoon_create_modelmaker.def.json
@@ -0,0 +1,96 @@
+{
+ "name": "Cocoon Create ModelMaker & Wanhao Duplicator i3 Mini",
+ "version": 2,
+ "inherits": "fdmprinter",
+ "metadata": {
+ "visible": true,
+ "author": "Samuel Pinches",
+ "manufacturer": "Cocoon Create / Wanhao",
+ "file_formats": "text/x-gcode",
+ "preferred_quality_type": "fine",
+ "machine_extruder_trains":
+ {
+ "0": "cocoon_create_modelmaker_extruder_0"
+ }
+ },
+ "overrides": {
+ "machine_name": {
+ "default_value": "Cocoon Create ModelMaker & Wanhao Duplicator i3 Mini"
+ },
+ "machine_start_gcode": {
+ "default_value": "; -- START GCODE --\nG21 ;set units to millimetres\nG90 ;set to absolute positioning\nM106 S0 ;set fan speed to zero (turned off)\nG28 ;home all axis\nG92 E0 ;zero the extruded length\nG1 Z1 F1000 ;move up slightly\nG1 X60.0 Z0 E9.0 F1000.0;intro line\nG1 X100.0 E21.5 F1000.0 ;continue line\nG92 E0 ;zero the extruded length again\n; -- end of START GCODE --"
+ },
+ "machine_end_gcode": {
+ "default_value": "; -- END GCODE --\nM104 S0 ;turn off nozzle heater\nG91 ;set to relative positioning\nG1 E-10 F300 ;retract the filament slightly\nG90 ;set to absolute positioning\nG28 X0 Y0 ;move to the XY-axis origin (Home)\nM84 ;turn off stepper motors\n; -- end of END GCODE --"
+ },
+ "machine_width": {
+ "default_value": 120
+ },
+ "machine_height": {
+ "default_value": 100
+ },
+ "machine_depth": {
+ "default_value": 135
+ },
+ "machine_heated_bed": {
+ "default_value": false
+ },
+ "machine_center_is_zero": {
+ "default_value": false
+ },
+ "gantry_height": {
+ "default_value": 10
+ },
+ "machine_gcode_flavor": {
+ "default_value": "RepRap (Marlin/Sprinter)"
+ },
+ "material_diameter": {
+ "default_value": 1.75
+ },
+ "material_print_temperature": {
+ "default_value": 220
+ },
+ "layer_height": {
+ "default_value": 0.15
+ },
+ "layer_height_0": {
+ "default_value": 0.2
+ },
+ "wall_thickness": {
+ "default_value": 1.2
+ },
+ "top_bottom_thickness": {
+ "default_value": 0.6
+ },
+ "speed_print": {
+ "default_value": 40
+ },
+ "speed_infill": {
+ "default_value": 40
+ },
+ "speed_wall": {
+ "default_value": 35
+ },
+ "speed_topbottom": {
+ "default_value": 35
+ },
+ "speed_travel": {
+ "default_value": 70
+ },
+ "speed_layer_0": {
+ "default_value": 20
+ },
+ "support_enable": {
+ "default_value": true
+ },
+ "retraction_enable": {
+ "default_value": true
+ },
+ "retraction_amount": {
+ "default_value": 7
+ },
+ "retraction_speed": {
+ "default_value": 40
+ }
+ }
+}
diff --git a/resources/definitions/jgaurora_a1.def.json b/resources/definitions/jgaurora_a1.def.json
new file mode 100644
index 0000000000..4fd2eb4994
--- /dev/null
+++ b/resources/definitions/jgaurora_a1.def.json
@@ -0,0 +1,96 @@
+{
+ "name": "JGAurora A1",
+ "version": 2,
+ "inherits": "fdmprinter",
+ "metadata": {
+ "visible": true,
+ "author": "Samuel Pinches",
+ "manufacturer": "JGAurora",
+ "file_formats": "text/x-gcode",
+ "preferred_quality_type": "fine",
+ "machine_extruder_trains":
+ {
+ "0": "jgaurora_a1_extruder_0"
+ }
+ },
+ "overrides": {
+ "machine_name": {
+ "default_value": "JGAurora A1"
+ },
+ "machine_start_gcode": {
+ "default_value": "; -- START GCODE --\nG21 ;set units to millimetres\nG90 ;set to absolute positioning\nM106 S0 ;set fan speed to zero (turned off)\nG28 ;home all axis\nM420 S1 ;turn on mesh bed levelling if enabled in firmware\nG92 E0 ;zero the extruded length\nG1 Z1 F1000 ;move up slightly\nG1 X60.0 Z0 E9.0 F1000.0;intro line\nG1 X100.0 E21.5 F1000.0 ;continue line\nG92 E0 ;zero the extruded length again\n; -- end of START GCODE --"
+ },
+ "machine_end_gcode": {
+ "default_value": "; -- END GCODE --\nM104 S0 ;turn off nozzle heater\nM140 S0 ;turn off bed heater\nG91 ;set to relative positioning\nG1 E-10 F300 ;retract the filament slightly\nG90 ;set to absolute positioning\nG28 X0 ;move to the X-axis origin (Home)\nG0 Y280 F600 ;bring the bed to the front for easy print removal\nM84 ;turn off stepper motors\n; -- end of END GCODE --"
+ },
+ "machine_width": {
+ "default_value": 300
+ },
+ "machine_height": {
+ "default_value": 300
+ },
+ "machine_depth": {
+ "default_value": 300
+ },
+ "machine_heated_bed": {
+ "default_value": true
+ },
+ "machine_center_is_zero": {
+ "default_value": false
+ },
+ "gantry_height": {
+ "default_value": 10
+ },
+ "machine_gcode_flavor": {
+ "default_value": "RepRap (Marlin/Sprinter)"
+ },
+ "material_diameter": {
+ "default_value": 1.75
+ },
+ "material_print_temperature": {
+ "default_value": 215
+ },
+ "material_bed_temperature": {
+ "default_value": 67
+ },
+ "layer_height": {
+ "default_value": 0.15
+ },
+ "layer_height_0": {
+ "default_value": 0.12
+ },
+ "wall_thickness": {
+ "default_value": 1.2
+ },
+ "speed_print": {
+ "default_value": 40
+ },
+ "speed_infill": {
+ "default_value": 40
+ },
+ "speed_wall": {
+ "default_value": 35
+ },
+ "speed_topbottom": {
+ "default_value": 35
+ },
+ "speed_travel": {
+ "default_value": 120
+ },
+ "speed_layer_0": {
+ "default_value": 12
+ },
+ "support_enable": {
+ "default_value": true
+ },
+ "retraction_enable": {
+ "default_value": true
+ },
+ "retraction_amount": {
+ "default_value": 6
+ },
+ "retraction_speed": {
+ "default_value": 40
+ }
+ }
+}
diff --git a/resources/definitions/jgaurora_a5.def.json b/resources/definitions/jgaurora_a5.def.json
new file mode 100644
index 0000000000..02d9a9db4f
--- /dev/null
+++ b/resources/definitions/jgaurora_a5.def.json
@@ -0,0 +1,98 @@
+{
+ "name": "JGAurora A5 & A5S",
+ "version": 2,
+ "inherits": "fdmprinter",
+ "metadata": {
+ "visible": true,
+ "author": "Samuel Pinches",
+ "manufacturer": "JGAurora",
+ "file_formats": "text/x-gcode",
+ "platform": "jgaurora_a5.stl",
+ "platform_offset": [-242, -101, 273],
+ "preferred_quality_type": "fine",
+ "machine_extruder_trains":
+ {
+ "0": "jgaurora_a5_extruder_0"
+ }
+ },
+ "overrides": {
+ "machine_name": {
+ "default_value": "JGAurora A5 & A5S"
+ },
+ "machine_start_gcode": {
+ "default_value": "; -- START GCODE --\nG21 ;set units to millimetres\nG90 ;set to absolute positioning\nM106 S0 ;set fan speed to zero (turned off)\nG28 ;home all axis\nM420 S1 ;turn on mesh bed levelling if enabled in firmware\nG92 E0 ;zero the extruded length\nG1 Z1 F1000 ;move up slightly\nG1 X60.0 Z0 E9.0 F1000.0;intro line\nG1 X100.0 E21.5 F1000.0 ;continue line\nG92 E0 ;zero the extruded length again\n; -- end of START GCODE --"
+ },
+ "machine_end_gcode": {
+ "default_value": "; -- END GCODE --\nM104 S0 ;turn off nozzle heater\nM140 S0 ;turn off bed heater\nG91 ;set to relative positioning\nG1 E-10 F300 ;retract the filament slightly\nG90 ;set to absolute positioning\nG28 X0 ;move to the X-axis origin (Home)\nG0 Y280 F600 ;bring the bed to the front for easy print removal\nM84 ;turn off stepper motors\n; -- end of END GCODE --"
+ },
+ "machine_width": {
+ "default_value": 300
+ },
+ "machine_height": {
+ "default_value": 320
+ },
+ "machine_depth": {
+ "default_value": 300
+ },
+ "machine_heated_bed": {
+ "default_value": true
+ },
+ "machine_center_is_zero": {
+ "default_value": false
+ },
+ "gantry_height": {
+ "default_value": 10
+ },
+ "machine_gcode_flavor": {
+ "default_value": "RepRap (Marlin/Sprinter)"
+ },
+ "material_diameter": {
+ "default_value": 1.75
+ },
+ "material_print_temperature": {
+ "default_value": 215
+ },
+ "material_bed_temperature": {
+ "default_value": 67
+ },
+ "layer_height": {
+ "default_value": 0.15
+ },
+ "layer_height_0": {
+ "default_value": 0.12
+ },
+ "wall_thickness": {
+ "default_value": 1.2
+ },
+ "speed_print": {
+ "default_value": 40
+ },
+ "speed_infill": {
+ "default_value": 40
+ },
+ "speed_wall": {
+ "default_value": 35
+ },
+ "speed_topbottom": {
+ "default_value": 35
+ },
+ "speed_travel": {
+ "default_value": 120
+ },
+ "speed_layer_0": {
+ "default_value": 12
+ },
+ "support_enable": {
+ "default_value": true
+ },
+ "retraction_enable": {
+ "default_value": true
+ },
+ "retraction_amount": {
+ "default_value": 8
+ },
+ "retraction_speed": {
+ "default_value": 45
+ }
+ }
+}
diff --git a/resources/definitions/jgaurora_z_603s.def.json b/resources/definitions/jgaurora_z_603s.def.json
new file mode 100644
index 0000000000..59e0ff129c
--- /dev/null
+++ b/resources/definitions/jgaurora_z_603s.def.json
@@ -0,0 +1,96 @@
+{
+ "name": "JGAurora Z-603S",
+ "version": 2,
+ "inherits": "fdmprinter",
+ "metadata": {
+ "visible": true,
+ "author": "Samuel Pinches",
+ "manufacturer": "JGAurora",
+ "file_formats": "text/x-gcode",
+ "preferred_quality_type": "fine",
+ "machine_extruder_trains":
+ {
+ "0": "jgaurora_z_603s_extruder_0"
+ }
+ },
+ "overrides": {
+ "machine_name": {
+ "default_value": "JGAurora Z-603S"
+ },
+ "machine_start_gcode": {
+ "default_value": "; -- START GCODE --\nG21 ;set units to millimetres\nG90 ;set to absolute positioning\nM106 S0 ;set fan speed to zero (turned off)\nG28 ;home all axis\nM420 S1 ;turn on mesh bed levelling if enabled in firmware\nG92 E0 ;zero the extruded length\nG1 Z1 F1000 ;move up slightly\nG1 X60.0 Z0 E9.0 F1000.0;intro line\nG1 X100.0 E21.5 F1000.0 ;continue line\nG92 E0 ;zero the extruded length again\n; -- end of START GCODE --"
+ },
+ "machine_end_gcode": {
+ "default_value": "; -- END GCODE --\nM104 S0 ;turn off nozzle heater\nM140 S0 ;turn off bed heater\nG91 ;set to relative positioning\nG1 E-10 F300 ;retract the filament slightly\nG90 ;set to absolute positioning\nG28 X0 ;move to the X-axis origin (Home)\nG0 Y280 F600 ;bring the bed to the front for easy print removal\nM84 ;turn off stepper motors\n; -- end of END GCODE --"
+ },
+ "machine_width": {
+ "default_value": 280
+ },
+ "machine_height": {
+ "default_value": 175
+ },
+ "machine_depth": {
+ "default_value": 180
+ },
+ "machine_heated_bed": {
+ "default_value": true
+ },
+ "machine_center_is_zero": {
+ "default_value": false
+ },
+ "gantry_height": {
+ "default_value": 10
+ },
+ "machine_gcode_flavor": {
+ "default_value": "RepRap (Marlin/Sprinter)"
+ },
+ "material_diameter": {
+ "default_value": 1.75
+ },
+ "material_print_temperature": {
+ "default_value": 210
+ },
+ "material_bed_temperature": {
+ "default_value": 55
+ },
+ "layer_height": {
+ "default_value": 0.15
+ },
+ "layer_height_0": {
+ "default_value": 0.2
+ },
+ "wall_thickness": {
+ "default_value": 1.2
+ },
+ "speed_print": {
+ "default_value": 60
+ },
+ "speed_infill": {
+ "default_value": 60
+ },
+ "speed_wall": {
+ "default_value": 30
+ },
+ "speed_topbottom": {
+ "default_value": 45
+ },
+ "speed_travel": {
+ "default_value": 125
+ },
+ "speed_layer_0": {
+ "default_value": 20
+ },
+ "support_enable": {
+ "default_value": true
+ },
+ "retraction_enable": {
+ "default_value": true
+ },
+ "retraction_amount": {
+ "default_value": 5
+ },
+ "retraction_speed": {
+ "default_value": 50
+ }
+ }
+}
diff --git a/resources/definitions/wanhao_d4s.def.json b/resources/definitions/wanhao_d4s.def.json
index 8788353e92..c1807923c6 100644
--- a/resources/definitions/wanhao_d4s.def.json
+++ b/resources/definitions/wanhao_d4s.def.json
@@ -39,10 +39,10 @@
"default_value": "RepRap (Marlin/Sprinter)"
},
"machine_start_gcode": {
- "default_value": "G21 ;metric values\n G90 ;absolute positioning\n M82 ;set extruder to absolute mode\n M107 ;start with the fan off\n G28 X0 Y0 ;move X/Y to min endstops\n G28 Z0 ;move Z to min endstops\n G1 Z15.0 F{travel_speed} ;move the platform down 15mm\n G92 E0 ;zero the extruded length\n G1 F200 E6 ;extrude 6 mm of feed stock\n G92 E0 ;zero the extruded length again\n G1 F{travel_speed} \n ;Put printing message on LCD screen\n M117 Printing..."
+ "default_value": "G21 ;metric values\n G90 ;absolute positioning\n M82 ;set extruder to absolute mode\n M107 ;start with the fan off\n G28 X0 Y0 ;move X/Y to min endstops\n G28 Z0 ;move Z to min endstops\n G1 Z15.0 F{speed_travel} ;move the platform down 15mm\n G92 E0 ;zero the extruded length\n G1 F200 E6 ;extrude 6 mm of feed stock\n G92 E0 ;zero the extruded length again\n G1 F{speed_travel} \n ;Put printing message on LCD screen\n M117 Printing..."
},
"machine_end_gcode": {
- "default_value": "M104 S0 ;extruder heater off \n G91 ;relative positioning\n G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\n G1 Z+0.5 E-5 X-20 Y-20 F{travel_speed} ;move Z up a bit and retract filament even more\n G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\n M84 ;steppers off\n G90 ;absolute positioning"
+ "default_value": "M104 S0 ;extruder heater off \n G91 ;relative positioning\n G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\n G1 Z+0.5 E-5 X-20 Y-20 F{speed_travel} ;move Z up a bit and retract filament even more\n G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\n M84 ;steppers off\n G90 ;absolute positioning"
}
}
}
diff --git a/resources/definitions/wanhao_d6.def.json b/resources/definitions/wanhao_d6.def.json
index 7ca3031124..c8a690d02c 100644
--- a/resources/definitions/wanhao_d6.def.json
+++ b/resources/definitions/wanhao_d6.def.json
@@ -42,10 +42,10 @@
"default_value": "RepRap (Marlin/Sprinter)"
},
"machine_start_gcode": {
- "default_value": "G21 ;metric values\n G90 ;absolute positioning\n M82 ;set extruder to absolute mode\n M107 ;start with the fan off\n G28 X0 Y0 ;move X/Y to min endstops\n G28 Z0 ;move Z to min endstops\n G1 Z15.0 F{travel_speed} ;move the platform down 15mm\n G92 E0 ;zero the extruded length\n G1 F200 E6 ;extrude 6 mm of feed stock\n G92 E0 ;zero the extruded length again\n G1 F{travel_speed} \n ;Put printing message on LCD screen\n M117 Printing..."
+ "default_value": "G21 ;metric values\n G90 ;absolute positioning\n M82 ;set extruder to absolute mode\n M107 ;start with the fan off\n G28 X0 Y0 ;move X/Y to min endstops\n G28 Z0 ;move Z to min endstops\n G1 Z15.0 F{speed_travel} ;move the platform down 15mm\n G92 E0 ;zero the extruded length\n G1 F200 E6 ;extrude 6 mm of feed stock\n G92 E0 ;zero the extruded length again\n G1 F{speed_travel} \n ;Put printing message on LCD screen\n M117 Printing..."
},
"machine_end_gcode": {
- "default_value": "M104 S0 ;extruder heater off \n G91 ;relative positioning\n G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\n G1 Z+0.5 E-5 X-20 Y-20 F{travel_speed} ;move Z up a bit and retract filament even more\n G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\n M84 ;steppers off\n G90 ;absolute positioning"
+ "default_value": "M104 S0 ;extruder heater off \n G91 ;relative positioning\n G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\n G1 Z+0.5 E-5 X-20 Y-20 F{speed_travel} ;move Z up a bit and retract filament even more\n G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\n M84 ;steppers off\n G90 ;absolute positioning"
}
}
}
diff --git a/resources/definitions/wanhao_d6_plus.def.json b/resources/definitions/wanhao_d6_plus.def.json
index f17b58db85..b3b5ed9b0a 100644
--- a/resources/definitions/wanhao_d6_plus.def.json
+++ b/resources/definitions/wanhao_d6_plus.def.json
@@ -39,10 +39,10 @@
"default_value": "RepRap (Marlin/Sprinter)"
},
"machine_start_gcode": {
- "default_value": "G21 ;metric values\n G90 ;absolute positioning\n M82 ;set extruder to absolute mode\n M107 ;start with the fan off\n G28 X0 Y0 ;move X/Y to min endstops\n G28 Z0 ;move Z to min endstops\n G1 Z15.0 F{travel_speed} ;move the platform down 15mm\n G92 E0 ;zero the extruded length\n G1 F200 E6 ;extrude 6 mm of feed stock\n G92 E0 ;zero the extruded length again\n G1 F{travel_speed} \n ;Put printing message on LCD screen\n M117 Printing..."
+ "default_value": "G21 ;metric values\n G90 ;absolute positioning\n M82 ;set extruder to absolute mode\n M107 ;start with the fan off\n G28 X0 Y0 ;move X/Y to min endstops\n G28 Z0 ;move Z to min endstops\n G1 Z15.0 F{speed_travel} ;move the platform down 15mm\n G92 E0 ;zero the extruded length\n G1 F200 E6 ;extrude 6 mm of feed stock\n G92 E0 ;zero the extruded length again\n G1 F{speed_travel} \n ;Put printing message on LCD screen\n M117 Printing..."
},
"machine_end_gcode": {
- "default_value": "M104 S0 ;extruder heater off \n G91 ;relative positioning\n G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\n G1 Z+0.5 E-5 X-20 Y-20 F{travel_speed} ;move Z up a bit and retract filament even more\n G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\n M84 ;steppers off\n G90 ;absolute positioning"
+ "default_value": "M104 S0 ;extruder heater off \n G91 ;relative positioning\n G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\n G1 Z+0.5 E-5 X-20 Y-20 F{speed_travel} ;move Z up a bit and retract filament even more\n G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\n M84 ;steppers off\n G90 ;absolute positioning"
}
}
}
diff --git a/resources/definitions/wanhao_duplicator5S.def.json b/resources/definitions/wanhao_duplicator5S.def.json
index 1d29b90249..b27a13fda8 100644
--- a/resources/definitions/wanhao_duplicator5S.def.json
+++ b/resources/definitions/wanhao_duplicator5S.def.json
@@ -42,10 +42,10 @@
"default_value": "RepRap (Marlin/Sprinter)"
},
"machine_start_gcode": {
- "default_value": "G21 ;metric values\n G90 ;absolute positioning\n M82 ;set extruder to absolute mode\n M107 ;start with the fan off\n G28 X0 Y0 ;move X/Y to min endstops\n G28 Z0 ;move Z to min endstops\n G1 Z15.0 F{travel_speed} ;move the platform down 15mm\n G92 E0 ;zero the extruded length\n G1 F200 E6 ;extrude 6 mm of feed stock\n G92 E0 ;zero the extruded length again\n G1 F{travel_speed} \n ;Put printing message on LCD screen\n M117 Printing..."
+ "default_value": "G21 ;metric values\n G90 ;absolute positioning\n M82 ;set extruder to absolute mode\n M107 ;start with the fan off\n G28 X0 Y0 ;move X/Y to min endstops\n G28 Z0 ;move Z to min endstops\n G1 Z15.0 F{speed_travel} ;move the platform down 15mm\n G92 E0 ;zero the extruded length\n G1 F200 E6 ;extrude 6 mm of feed stock\n G92 E0 ;zero the extruded length again\n G1 F{speed_travel} \n ;Put printing message on LCD screen\n M117 Printing..."
},
"machine_end_gcode": {
- "default_value": "M104 S0 ;extruder heater off \n G91 ;relative positioning\n G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\n G1 Z+0.5 E-5 X-20 Y-20 F{travel_speed} ;move Z up a bit and retract filament even more\n G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\n M84 ;steppers off\n G90 ;absolute positioning"
+ "default_value": "M104 S0 ;extruder heater off \n G91 ;relative positioning\n G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\n G1 Z+0.5 E-5 X-20 Y-20 F{speed_travel} ;move Z up a bit and retract filament even more\n G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\n M84 ;steppers off\n G90 ;absolute positioning"
}
}
}
diff --git a/resources/definitions/wanhao_duplicator5Smini.def.json b/resources/definitions/wanhao_duplicator5Smini.def.json
index e7f9359cf1..e3ef0b92fe 100644
--- a/resources/definitions/wanhao_duplicator5Smini.def.json
+++ b/resources/definitions/wanhao_duplicator5Smini.def.json
@@ -39,10 +39,10 @@
"default_value": "RepRap (Marlin/Sprinter)"
},
"machine_start_gcode": {
- "default_value": "G21 ;metric values\n G90 ;absolute positioning\n M82 ;set extruder to absolute mode\n M107 ;start with the fan off\n G28 X0 Y0 ;move X/Y to min endstops\n G28 Z0 ;move Z to min endstops\n G1 Z15.0 F{travel_speed} ;move the platform down 15mm\n G92 E0 ;zero the extruded length\n G1 F200 E6 ;extrude 6 mm of feed stock\n G92 E0 ;zero the extruded length again\n G1 F{travel_speed} \n ;Put printing message on LCD screen\n M117 Printing..."
+ "default_value": "G21 ;metric values\n G90 ;absolute positioning\n M82 ;set extruder to absolute mode\n M107 ;start with the fan off\n G28 X0 Y0 ;move X/Y to min endstops\n G28 Z0 ;move Z to min endstops\n G1 Z15.0 F{speed_travel} ;move the platform down 15mm\n G92 E0 ;zero the extruded length\n G1 F200 E6 ;extrude 6 mm of feed stock\n G92 E0 ;zero the extruded length again\n G1 F{speed_travel} \n ;Put printing message on LCD screen\n M117 Printing..."
},
"machine_end_gcode": {
- "default_value": "M104 S0 ;extruder heater off \n G91 ;relative positioning\n G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\n G1 Z+0.5 E-5 X-20 Y-20 F{travel_speed} ;move Z up a bit and retract filament even more\n G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\n M84 ;steppers off\n G90 ;absolute positioning"
+ "default_value": "M104 S0 ;extruder heater off \n G91 ;relative positioning\n G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\n G1 Z+0.5 E-5 X-20 Y-20 F{speed_travel} ;move Z up a bit and retract filament even more\n G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\n M84 ;steppers off\n G90 ;absolute positioning"
}
}
}
diff --git a/resources/definitions/wanhao_i3.def.json b/resources/definitions/wanhao_i3.def.json
index 15121f8b8b..42b19c8748 100644
--- a/resources/definitions/wanhao_i3.def.json
+++ b/resources/definitions/wanhao_i3.def.json
@@ -39,10 +39,10 @@
"default_value": "RepRap (Marlin/Sprinter)"
},
"machine_start_gcode": {
- "default_value": "G21 ;metric values\n G90 ;absolute positioning\n M82 ;set extruder to absolute mode\n M107 ;start with the fan off\n G28 X0 Y0 ;move X/Y to min endstops\n G28 Z0 ;move Z to min endstops\n G1 Z15.0 F{travel_speed} ;move the platform down 15mm\n G92 E0 ;zero the extruded length\n G1 F200 E6 ;extrude 6 mm of feed stock\n G92 E0 ;zero the extruded length again\n G1 F{travel_speed} \n ;Put printing message on LCD screen\n M117 Printing..."
+ "default_value": "G21 ;metric values\n G90 ;absolute positioning\n M82 ;set extruder to absolute mode\n M107 ;start with the fan off\n G28 X0 Y0 ;move X/Y to min endstops\n G28 Z0 ;move Z to min endstops\n G1 Z15.0 F{speed_travel} ;move the platform down 15mm\n G92 E0 ;zero the extruded length\n G1 F200 E6 ;extrude 6 mm of feed stock\n G92 E0 ;zero the extruded length again\n G1 F{speed_travel} \n ;Put printing message on LCD screen\n M117 Printing..."
},
"machine_end_gcode": {
- "default_value": "M104 S0 ;extruder heater off \n G91 ;relative positioning\n G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\n G1 Z+0.5 E-5 X-20 Y-20 F{travel_speed} ;move Z up a bit and retract filament even more\n G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\n M84 ;steppers off\n G90 ;absolute positioning"
+ "default_value": "M104 S0 ;extruder heater off \n G91 ;relative positioning\n G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\n G1 Z+0.5 E-5 X-20 Y-20 F{speed_travel} ;move Z up a bit and retract filament even more\n G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\n M84 ;steppers off\n G90 ;absolute positioning"
}
}
}
diff --git a/resources/definitions/wanhao_i3mini.def.json b/resources/definitions/wanhao_i3mini.def.json
index 057fca81a6..0c70391c27 100644
--- a/resources/definitions/wanhao_i3mini.def.json
+++ b/resources/definitions/wanhao_i3mini.def.json
@@ -39,10 +39,10 @@
"default_value": "RepRap (Marlin/Sprinter)"
},
"machine_start_gcode": {
- "default_value": "G21 ;metric values\n G90 ;absolute positioning\n M82 ;set extruder to absolute mode\n M107 ;start with the fan off\n G28 X0 Y0 ;move X/Y to min endstops\n G28 Z0 ;move Z to min endstops\n G1 Z15.0 F{travel_speed} ;move the platform down 15mm\n G92 E0 ;zero the extruded length\n G1 F200 E6 ;extrude 6 mm of feed stock\n G92 E0 ;zero the extruded length again\n G1 F{travel_speed} \n ;Put printing message on LCD screen\n M117 Printing..."
+ "default_value": "G21 ;metric values\n G90 ;absolute positioning\n M82 ;set extruder to absolute mode\n M107 ;start with the fan off\n G28 X0 Y0 ;move X/Y to min endstops\n G28 Z0 ;move Z to min endstops\n G1 Z15.0 F{speed_travel} ;move the platform down 15mm\n G92 E0 ;zero the extruded length\n G1 F200 E6 ;extrude 6 mm of feed stock\n G92 E0 ;zero the extruded length again\n G1 F{speed_travel} \n ;Put printing message on LCD screen\n M117 Printing..."
},
"machine_end_gcode": {
- "default_value": "M104 S0 ;extruder heater off \n G91 ;relative positioning\n G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\n G1 Z+0.5 E-5 X-20 Y-20 F{travel_speed} ;move Z up a bit and retract filament even more\n G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\n M84 ;steppers off\n G90 ;absolute positioning"
+ "default_value": "M104 S0 ;extruder heater off \n G91 ;relative positioning\n G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\n G1 Z+0.5 E-5 X-20 Y-20 F{speed_travel} ;move Z up a bit and retract filament even more\n G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\n M84 ;steppers off\n G90 ;absolute positioning"
}
}
}
diff --git a/resources/definitions/wanhao_i3plus.def.json b/resources/definitions/wanhao_i3plus.def.json
index 2b705c6ff5..e454a40ae1 100644
--- a/resources/definitions/wanhao_i3plus.def.json
+++ b/resources/definitions/wanhao_i3plus.def.json
@@ -39,10 +39,10 @@
"default_value": "RepRap (Marlin/Sprinter)"
},
"machine_start_gcode": {
- "default_value": "G21 ;metric values\n G90 ;absolute positioning\n M82 ;set extruder to absolute mode\n M107 ;start with the fan off\n G28 X0 Y0 ;move X/Y to min endstops\n G28 Z0 ;move Z to min endstops\n G1 Z15.0 F{travel_speed} ;move the platform down 15mm\n G92 E0 ;zero the extruded length\n G1 F200 E6 ;extrude 6 mm of feed stock\n G92 E0 ;zero the extruded length again\n G1 F{travel_speed} \n ;Put printing message on LCD screen\n M117 Printing..."
+ "default_value": "G21 ;metric values\n G90 ;absolute positioning\n M82 ;set extruder to absolute mode\n M107 ;start with the fan off\n G28 X0 Y0 ;move X/Y to min endstops\n G28 Z0 ;move Z to min endstops\n G1 Z15.0 F{speed_travel} ;move the platform down 15mm\n G92 E0 ;zero the extruded length\n G1 F200 E6 ;extrude 6 mm of feed stock\n G92 E0 ;zero the extruded length again\n G1 F{speed_travel} \n ;Put printing message on LCD screen\n M117 Printing..."
},
"machine_end_gcode": {
- "default_value": "M104 S0 ;extruder heater off \n G91 ;relative positioning\n G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\n G1 Z+0.5 E-5 X-20 Y-20 F{travel_speed} ;move Z up a bit and retract filament even more\n G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\n M84 ;steppers off\n G90 ;absolute positioning"
+ "default_value": "M104 S0 ;extruder heater off \n G91 ;relative positioning\n G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\n G1 Z+0.5 E-5 X-20 Y-20 F{speed_travel} ;move Z up a bit and retract filament even more\n G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\n M84 ;steppers off\n G90 ;absolute positioning"
}
}
}
diff --git a/resources/extruders/alfawise_u20_extruder_0.def.json b/resources/extruders/alfawise_u20_extruder_0.def.json
new file mode 100644
index 0000000000..2fbe3f1772
--- /dev/null
+++ b/resources/extruders/alfawise_u20_extruder_0.def.json
@@ -0,0 +1,16 @@
+{
+ "id": "alfawise_u20_extruder_0",
+ "version": 2,
+ "name": "Extruder 1",
+ "inherits": "fdmextruder",
+ "metadata": {
+ "machine": "alfawise_u20",
+ "position": "0"
+ },
+
+ "overrides": {
+ "extruder_nr": { "default_value": 0 },
+ "machine_nozzle_size": { "default_value": 0.4 },
+ "material_diameter": { "default_value": 1.75 }
+ }
+}
\ No newline at end of file
diff --git a/resources/extruders/bibo2_dual_extruder_0.def.json b/resources/extruders/bibo2_dual_extruder_0.def.json
new file mode 100644
index 0000000000..f83801fa0c
--- /dev/null
+++ b/resources/extruders/bibo2_dual_extruder_0.def.json
@@ -0,0 +1,46 @@
+{
+ "id": "BIBO1",
+ "version": 2,
+ "name": "Extruder 1",
+ "inherits": "fdmextruder",
+ "metadata": {
+ "machine": "BIBO2 dual",
+ "position": "0"
+ },
+ "overrides": {
+ "extruder_nr": {
+ "default_value": 0,
+ "maximum_value": "1"
+ },
+ "material_diameter": {
+ "default_value": 1.75
+ },
+ "machine_nozzle_size": {
+ "default_value": 0.4
+ },
+ "machine_nozzle_offset_x": {
+ "default_value": 0.0
+ },
+ "machine_nozzle_offset_y": {
+ "default_value": 0.0
+ },
+ "machine_extruder_start_pos_abs": {
+ "default_value": true
+ },
+ "machine_extruder_start_pos_x": {
+ "value": "prime_tower_position_x"
+ },
+ "machine_extruder_start_pos_y": {
+ "value": "prime_tower_position_y"
+ },
+ "machine_extruder_end_pos_abs": {
+ "default_value": true
+ },
+ "machine_extruder_end_pos_x": {
+ "value": "prime_tower_position_x"
+ },
+ "machine_extruder_end_pos_y": {
+ "value": "prime_tower_position_y"
+ }
+ }
+}
diff --git a/resources/extruders/bibo2_dual_extruder_1.def.json b/resources/extruders/bibo2_dual_extruder_1.def.json
new file mode 100644
index 0000000000..5f479ba54b
--- /dev/null
+++ b/resources/extruders/bibo2_dual_extruder_1.def.json
@@ -0,0 +1,46 @@
+{
+ "id": "BIBO2",
+ "version": 2,
+ "name": "Extruder 2",
+ "inherits": "fdmextruder",
+ "metadata": {
+ "machine": "BIBO2 dual",
+ "position": "1"
+ },
+ "overrides": {
+ "extruder_nr": {
+ "default_value": 1,
+ "maximum_value": "1"
+ },
+ "material_diameter": {
+ "default_value": 1.75
+ },
+ "machine_nozzle_size": {
+ "default_value": 0.4
+ },
+ "machine_nozzle_offset_x": {
+ "default_value": 0.0
+ },
+ "machine_nozzle_offset_y": {
+ "default_value": 0.0
+ },
+ "machine_extruder_start_pos_abs": {
+ "default_value": true
+ },
+ "machine_extruder_start_pos_x": {
+ "value": "prime_tower_position_x"
+ },
+ "machine_extruder_start_pos_y": {
+ "value": "prime_tower_position_y"
+ },
+ "machine_extruder_end_pos_abs": {
+ "default_value": true
+ },
+ "machine_extruder_end_pos_x": {
+ "value": "prime_tower_position_x"
+ },
+ "machine_extruder_end_pos_y": {
+ "value": "prime_tower_position_y"
+ }
+ }
+}
diff --git a/resources/extruders/cocoon_create_modelmaker_extruder_0.def.json b/resources/extruders/cocoon_create_modelmaker_extruder_0.def.json
new file mode 100644
index 0000000000..26d847483d
--- /dev/null
+++ b/resources/extruders/cocoon_create_modelmaker_extruder_0.def.json
@@ -0,0 +1,16 @@
+{
+ "id": "cocoon_create_modelmaker_extruder_0",
+ "version": 2,
+ "name": "Extruder 1",
+ "inherits": "fdmextruder",
+ "metadata": {
+ "machine": "cocoon_create_modelmaker",
+ "position": "0"
+ },
+
+ "overrides": {
+ "extruder_nr": { "default_value": 0 },
+ "machine_nozzle_size": { "default_value": 0.4 },
+ "material_diameter": { "default_value": 1.75 }
+ }
+}
diff --git a/resources/extruders/jgaurora_a1_extruder_0.def.json b/resources/extruders/jgaurora_a1_extruder_0.def.json
new file mode 100644
index 0000000000..71742b734a
--- /dev/null
+++ b/resources/extruders/jgaurora_a1_extruder_0.def.json
@@ -0,0 +1,16 @@
+{
+ "id": "jgaurora_a1_extruder_0",
+ "version": 2,
+ "name": "Extruder 1",
+ "inherits": "fdmextruder",
+ "metadata": {
+ "machine": "jgaurora_a1",
+ "position": "0"
+ },
+
+ "overrides": {
+ "extruder_nr": { "default_value": 0 },
+ "machine_nozzle_size": { "default_value": 0.4 },
+ "material_diameter": { "default_value": 1.75 }
+ }
+}
diff --git a/resources/extruders/jgaurora_a5_extruder_0.def.json b/resources/extruders/jgaurora_a5_extruder_0.def.json
new file mode 100644
index 0000000000..fbc6ba77e6
--- /dev/null
+++ b/resources/extruders/jgaurora_a5_extruder_0.def.json
@@ -0,0 +1,16 @@
+{
+ "id": "jgaurora_a5_extruder_0",
+ "version": 2,
+ "name": "Extruder 1",
+ "inherits": "fdmextruder",
+ "metadata": {
+ "machine": "jgaurora_a5",
+ "position": "0"
+ },
+
+ "overrides": {
+ "extruder_nr": { "default_value": 0 },
+ "machine_nozzle_size": { "default_value": 0.4 },
+ "material_diameter": { "default_value": 1.75 }
+ }
+}
diff --git a/resources/extruders/jgaurora_z_603s_extruder_0.def.json b/resources/extruders/jgaurora_z_603s_extruder_0.def.json
new file mode 100644
index 0000000000..987425b28a
--- /dev/null
+++ b/resources/extruders/jgaurora_z_603s_extruder_0.def.json
@@ -0,0 +1,16 @@
+{
+ "id": "jgaurora_z_603s_extruder_0",
+ "version": 2,
+ "name": "Extruder 1",
+ "inherits": "fdmextruder",
+ "metadata": {
+ "machine": "jgaurora_z_603s",
+ "position": "0"
+ },
+
+ "overrides": {
+ "extruder_nr": { "default_value": 0 },
+ "machine_nozzle_size": { "default_value": 0.4 },
+ "material_diameter": { "default_value": 1.75 }
+ }
+}
diff --git a/resources/meshes/jgaurora_a5.stl b/resources/meshes/jgaurora_a5.stl
new file mode 100644
index 0000000000..c525b03649
Binary files /dev/null and b/resources/meshes/jgaurora_a5.stl differ
diff --git a/resources/qml/Account/GeneralOperations.qml b/resources/qml/Account/GeneralOperations.qml
index 4614c4ba88..b9f1025d5e 100644
--- a/resources/qml/Account/GeneralOperations.qml
+++ b/resources/qml/Account/GeneralOperations.qml
@@ -11,20 +11,16 @@ Row
{
spacing: UM.Theme.getSize("default_margin").width
- Cura.ActionButton
+ Cura.SecondaryButton
{
width: UM.Theme.getSize("account_button").width
height: UM.Theme.getSize("account_button").height
text: catalog.i18nc("@button", "Create account")
- color: UM.Theme.getColor("secondary")
- hoverColor: UM.Theme.getColor("secondary")
- textColor: UM.Theme.getColor("main_window_header_button_text_active")
- textHoverColor: UM.Theme.getColor("main_window_header_button_text_active")
onClicked: Qt.openUrlExternally("https://account.ultimaker.com/app/create")
fixedWidthMode: true
}
- Cura.ActionButton
+ Cura.PrimaryButton
{
width: UM.Theme.getSize("account_button").width
height: UM.Theme.getSize("account_button").height
diff --git a/resources/qml/Account/UserOperations.qml b/resources/qml/Account/UserOperations.qml
index c167813425..b9ffa395d6 100644
--- a/resources/qml/Account/UserOperations.qml
+++ b/resources/qml/Account/UserOperations.qml
@@ -11,20 +11,16 @@ Row
{
spacing: UM.Theme.getSize("default_margin").width
- Cura.ActionButton
+ Cura.SecondaryButton
{
width: UM.Theme.getSize("account_button").width
height: UM.Theme.getSize("account_button").height
text: catalog.i18nc("@button", "Manage account")
- color: UM.Theme.getColor("secondary")
- hoverColor: UM.Theme.getColor("secondary")
- textColor: UM.Theme.getColor("main_window_header_button_text_active")
- textHoverColor: UM.Theme.getColor("main_window_header_button_text_active")
onClicked: Qt.openUrlExternally("https://account.ultimaker.com")
fixedWidthMode: true
}
- Cura.ActionButton
+ Cura.PrimaryButton
{
width: UM.Theme.getSize("account_button").width
height: UM.Theme.getSize("account_button").height
diff --git a/resources/qml/ActionButton.qml b/resources/qml/ActionButton.qml
index 08f4d9d679..732858c67f 100644
--- a/resources/qml/ActionButton.qml
+++ b/resources/qml/ActionButton.qml
@@ -3,28 +3,35 @@
import QtQuick 2.7
import QtQuick.Controls 2.1
-import QtQuick.Layouts 1.3
+
+import QtGraphicalEffects 1.0 // For the dropshadow
import UM 1.1 as UM
Button
{
id: button
- property alias cursorShape: mouseArea.cursorShape
property alias iconSource: buttonIcon.source
property alias iconSourceRight: buttonIconRight.source
property alias textFont: buttonText.font
property alias cornerRadius: backgroundRect.radius
property alias tooltip: tooltip.text
- property var color: UM.Theme.getColor("primary")
- property var hoverColor: UM.Theme.getColor("primary_hover")
- property var disabledColor: color
- property var textColor: UM.Theme.getColor("button_text")
- property var textHoverColor: UM.Theme.getColor("button_text_hover")
- property var textDisabledColor: textColor
- property var outlineColor: color
- property var outlineHoverColor: hoverColor
- property var outlineDisabledColor: outlineColor
+
+ 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
+
+ hoverEnabled: true
+
+ 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.
@@ -84,6 +91,19 @@ Button
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
@@ -91,12 +111,4 @@ Button
delay: 500
visible: text != "" && button.hovered
}
-
- MouseArea
- {
- id: mouseArea
- anchors.fill: parent
- onPressed: mouse.accepted = false
- hoverEnabled: true
- }
}
diff --git a/resources/qml/ActionPanel/OutputDevicesActionButton.qml b/resources/qml/ActionPanel/OutputDevicesActionButton.qml
index be79a1893e..2111038cfc 100644
--- a/resources/qml/ActionPanel/OutputDevicesActionButton.qml
+++ b/resources/qml/ActionPanel/OutputDevicesActionButton.qml
@@ -12,7 +12,7 @@ Item
{
id: widget
- Cura.ActionButton
+ Cura.PrimaryButton
{
id: saveToButton
height: parent.height
@@ -42,6 +42,9 @@ Item
id: deviceSelectionMenu
height: parent.height
+ shadowEnabled: true
+ shadowColor: UM.Theme.getColor("primary_shadow")
+
anchors
{
top: parent.top
@@ -65,7 +68,7 @@ Item
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
- contentItem: Column
+ contentItem: ColumnLayout
{
Repeater
{
@@ -77,7 +80,7 @@ Item
color: "transparent"
cornerRadius: 0
hoverColor: UM.Theme.getColor("primary")
-
+ Layout.fillWidth: true
onClicked:
{
UM.OutputDeviceManager.setActiveDevice(model.id)
@@ -91,10 +94,7 @@ Item
{
opacity: visible ? 1 : 0
Behavior on opacity { NumberAnimation { duration: 100 } }
- radius: UM.Theme.getSize("default_radius").width
color: UM.Theme.getColor("action_panel_secondary")
- border.color: UM.Theme.getColor("lining")
- border.width: UM.Theme.getSize("default_lining").width
}
}
}
diff --git a/resources/qml/ActionPanel/OutputProcessWidget.qml b/resources/qml/ActionPanel/OutputProcessWidget.qml
index 3c4386f079..45cb1fdb41 100644
--- a/resources/qml/ActionPanel/OutputProcessWidget.qml
+++ b/resources/qml/ActionPanel/OutputProcessWidget.qml
@@ -18,6 +18,7 @@ Column
id: widget
spacing: UM.Theme.getSize("thin_margin").height
+ property bool preSlicedData: PrintInformation.preSliced
UM.I18nCatalog
{
@@ -48,7 +49,7 @@ Column
id: estimatedTime
width: parent.width
- text: PrintInformation.currentPrintTime.getDisplayString(UM.DurationFormat.Long)
+ text: preSlicedData ? catalog.i18nc("@label", "No time estimation available") : PrintInformation.currentPrintTime.getDisplayString(UM.DurationFormat.Long)
source: UM.Theme.getIcon("clock")
font: UM.Theme.getFont("small")
}
@@ -63,6 +64,10 @@ Column
text:
{
+ if (preSlicedData)
+ {
+ return catalog.i18nc("@label", "No cost estimation available")
+ }
var totalLengths = 0
var totalWeights = 0
if (printMaterialLengths)
@@ -86,6 +91,7 @@ Column
PrintInformationWidget
{
id: printInformationPanel
+ visible: !preSlicedData
anchors
{
@@ -101,20 +107,18 @@ Column
spacing: UM.Theme.getSize("default_margin").width
width: parent.width
- Cura.ActionButton
+ Cura.SecondaryButton
{
id: previewStageShortcut
- leftPadding: UM.Theme.getSize("default_margin").width
- rightPadding: UM.Theme.getSize("default_margin").width
height: UM.Theme.getSize("action_panel_button").height
text: catalog.i18nc("@button", "Preview")
- color: UM.Theme.getColor("secondary")
- hoverColor: UM.Theme.getColor("secondary")
- textColor: UM.Theme.getColor("primary")
- textHoverColor: UM.Theme.getColor("text")
+
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
diff --git a/resources/qml/ActionPanel/SliceProcessWidget.qml b/resources/qml/ActionPanel/SliceProcessWidget.qml
index 2d4a7b6b89..3329ac4b23 100644
--- a/resources/qml/ActionPanel/SliceProcessWidget.qml
+++ b/resources/qml/ActionPanel/SliceProcessWidget.qml
@@ -40,9 +40,21 @@ Column
}
}
+ Label
+ {
+ id: autoSlicingLabel
+ width: parent.width
+ visible: prepareButtons.autoSlice && widget.backendState == UM.Backend.Processing
+
+ text: catalog.i18nc("@label:PrintjobStatus", "Auto slicing...")
+ color: UM.Theme.getColor("text")
+ font: UM.Theme.getFont("very_small")
+ renderType: Text.NativeRendering
+ }
+
Cura.IconLabel
{
- id: message
+ id: unableToSliceMessage
width: parent.width
visible: widget.backendState == UM.Backend.Error
@@ -81,38 +93,41 @@ Column
}
}
- Cura.ActionButton
- {
- id: prepareButton
- width: parent.width
- height: UM.Theme.getSize("action_panel_button").height
- fixedWidthMode: true
- text:
- {
- if ([UM.Backend.NotStarted, UM.Backend.Error].indexOf(widget.backendState) != -1)
- {
- return catalog.i18nc("@button", "Slice")
- }
- if (autoSlice)
- {
- return catalog.i18nc("@button", "Auto slicing...")
- }
- return catalog.i18nc("@button", "Cancel")
- }
- enabled: !autoSlice && !disabledSlice
+ Item
+ {
+ id: prepareButtons
// Get the current value from the preferences
property bool autoSlice: UM.Preferences.getValue("general/auto_slice")
// Disable the slice process when
- property bool disabledSlice: [UM.Backend.Done, UM.Backend.Error].indexOf(widget.backendState) != -1
- disabledColor: disabledSlice ? UM.Theme.getColor("action_button_disabled") : "transparent"
- textDisabledColor: disabledSlice ? UM.Theme.getColor("action_button_disabled_text") : UM.Theme.getColor("primary")
- outlineDisabledColor: disabledSlice ? UM.Theme.getColor("action_button_disabled_border") : "transparent"
+ width: parent.width
+ height: UM.Theme.getSize("action_panel_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()
+ }
- 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
{
@@ -120,7 +135,7 @@ Column
onPreferenceChanged:
{
var autoSlice = UM.Preferences.getValue("general/auto_slice")
- prepareButton.autoSlice = autoSlice
+ prepareButtons.autoSlice = autoSlice
}
}
diff --git a/resources/qml/BorderGroup.qml b/resources/qml/BorderGroup.qml
new file mode 100644
index 0000000000..38ad9fadff
--- /dev/null
+++ b/resources/qml/BorderGroup.qml
@@ -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"
+}
diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml
index 2814bb9eb2..36f5758fa3 100644
--- a/resources/qml/Cura.qml
+++ b/resources/qml/Cura.qml
@@ -6,6 +6,7 @@ import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtQuick.Layouts 1.1
import QtQuick.Dialogs 1.2
+import QtGraphicalEffects 1.0
import UM 1.3 as UM
import Cura 1.1 as Cura
@@ -153,7 +154,31 @@ UM.MainWindow
}
visible: stageMenu.source != ""
height: Math.round(UM.Theme.getSize("stage_menu").height / 2)
- color: UM.Theme.getColor("main_window_header_background")
+
+ LinearGradient
+ {
+ 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")
+ }
+ }
+ }
}
Connections
@@ -187,7 +212,7 @@ UM.MainWindow
verticalCenter: parent.verticalCenter
left: parent.left
}
- visible: CuraApplication.platformActivity
+ visible: CuraApplication.platformActivity && !PrintInformation.preSliced
}
ObjectsList
diff --git a/resources/qml/Dialogs/AddMachineDialog.qml b/resources/qml/Dialogs/AddMachineDialog.qml
index aa160acd4d..8b2b9d1868 100644
--- a/resources/qml/Dialogs/AddMachineDialog.qml
+++ b/resources/qml/Dialogs/AddMachineDialog.qml
@@ -298,7 +298,6 @@ UM.Dialog
id: machineName
text: getMachineName()
width: Math.floor(parent.width * 0.75)
- implicitWidth: UM.Theme.getSize("standard_list_input").width
maximumLength: 40
//validator: Cura.MachineNameValidator { } //TODO: Gives a segfault in PyQt5.6. For now, we must use a signal on text changed.
validator: RegExpValidator
diff --git a/resources/qml/ExpandableComponent.qml b/resources/qml/ExpandableComponent.qml
index 707b947c7f..897d44d941 100644
--- a/resources/qml/ExpandableComponent.qml
+++ b/resources/qml/ExpandableComponent.qml
@@ -2,6 +2,9 @@ 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 3 major sub components:
// * The headerItem; Always visible and should hold some info about what happens if the component is expanded
@@ -10,6 +13,14 @@ import UM 1.2 as UM
Item
{
id: base
+
+ // Enumeration with the different possible alignments of the popup with respect of the headerItem
+ enum PopupAlignment
+ {
+ AlignLeft,
+ AlignRight
+ }
+
// The headerItem holds the QML item that is always displayed.
property alias headerItem: headerItemLoader.sourceComponent
@@ -21,6 +32,9 @@ Item
property color headerBackgroundColor: UM.Theme.getColor("action_button")
property color headerHoverColor: UM.Theme.getColor("action_button_hovered")
+ // Defines the alignment of the popup with respect of the headerItem, by default to the right
+ property int popupAlignment: ExpandableComponent.PopupAlignment.AlignRight
+
// How much spacing is needed around the popupItem
property alias popupPadding: popup.padding
@@ -52,6 +66,12 @@ Item
// Change the popupItem close behaviour
property alias popupClosePolicy : popup.closePolicy
+ property alias headerShadowColor: shadow.color
+
+ property alias enableHeaderShadow: shadow.visible
+
+ property int shadowOffset: 2
+
function togglePopup()
{
if(popup.visible)
@@ -129,8 +149,8 @@ Item
sourceSize.height: height
visible: source != ""
width: height
- height: 0.2 * base.height
- color: "black"
+ height: Math.round(0.2 * base.height)
+ color: UM.Theme.getColor("text")
}
MouseArea
@@ -143,23 +163,40 @@ Item
onExited: background.color = headerBackgroundColor
}
}
+ 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: popup
// Ensure that the popup is located directly below the headerItem
- y: headerItemLoader.height + 2 * background.padding + popupSpacingY
+ y: headerItemLoader.height + 2 * background.padding + base.shadowOffset + popupSpacingY
- // Make the popup right aligned with the rest. The 3x padding is due to left, right and padding between
- // the button & text.
- x: -width + collapseButton.width + headerItemLoader.width + 3 * background.padding
+ // Make the popup aligned with the rest, using the property popupAlignment 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: popupAlignment == ExpandableComponent.PopupAlignment.AlignRight ? -width + collapseButton.width + headerItemLoader.width + 3 * background.padding : 0
padding: UM.Theme.getSize("default_margin").width
closePolicy: Popup.CloseOnPressOutsideParent
- background: Rectangle
+ background: Cura.RoundedRectangle
{
+ cornerSide: Cura.RoundedRectangle.Direction.Down
color: popupBackgroundColor
+ border.width: UM.Theme.getSize("default_lining").width
+ border.color: UM.Theme.getColor("lining")
+ radius: UM.Theme.getSize("default_radius").width
}
}
}
diff --git a/resources/qml/ExtruderButton.qml b/resources/qml/ExtruderButton.qml
index 8a1f0af44a..feb399d528 100644
--- a/resources/qml/ExtruderButton.qml
+++ b/resources/qml/ExtruderButton.qml
@@ -7,7 +7,7 @@ import QtQuick.Controls 2.0
import UM 1.2 as UM
import Cura 1.0 as Cura
-Button
+Cura.ToolbarButton
{
id: base
@@ -18,11 +18,9 @@ Button
checked: Cura.ExtruderManager.selectedObjectExtruders.indexOf(extruder.id) != -1
enabled: UM.Selection.hasSelection && extruder.stack.isEnabled
- background: Item {}
- contentItem: ExtruderIcon
+ toolItem: ExtruderIcon
{
- width: UM.Theme.getSize("button_icon").width
- materialColor: model.color
+ materialColor: extruder.color
extruderEnabled: extruder.stack.isEnabled
property int index: extruder.index
}
@@ -30,6 +28,6 @@ Button
onClicked:
{
forceActiveFocus() //First grab focus, so all the text fields are updated
- CuraActions.setExtruderForSelection(extruder.id);
+ CuraActions.setExtruderForSelection(extruder.id)
}
}
diff --git a/resources/qml/ExtruderIcon.qml b/resources/qml/ExtruderIcon.qml
index c103ee245c..c1a202050b 100644
--- a/resources/qml/ExtruderIcon.qml
+++ b/resources/qml/ExtruderIcon.qml
@@ -16,6 +16,7 @@ Item
property color materialColor
property alias textColor: extruderNumberText.color
property bool extruderEnabled: true
+
UM.RecolorImage
{
id: mainIcon
@@ -50,8 +51,6 @@ Item
anchors.centerIn: parent
text: index + 1
font: UM.Theme.getFont("extruder_icon")
- width: contentWidth
- height: contentHeight
visible: extruderEnabled
renderType: Text.NativeRendering
horizontalAlignment: Text.AlignHCenter
diff --git a/resources/qml/IconLabel.qml b/resources/qml/IconLabel.qml
index 7c90382892..0941254e7b 100644
--- a/resources/qml/IconLabel.qml
+++ b/resources/qml/IconLabel.qml
@@ -16,32 +16,37 @@ Item
property alias source: icon.source
property alias color: label.color
property alias font: label.font
+ property alias iconSize: icon.width
- height: childrenRect.height
+ implicitHeight: icon.height
UM.RecolorImage
{
id: icon
anchors.left: parent.left
+ anchors.verticalCenter: parent.verticalCenter
- source: UM.Theme.getIcon("dot")
+ source: ""
width: UM.Theme.getSize("section_icon").width
- height: UM.Theme.getSize("section_icon").height
+ height: width
sourceSize.width: width
sourceSize.height: height
color: label.color
+ visible: source != ""
}
Label
{
id: label
- anchors.left: icon.right
+ anchors.left: icon.visible ? icon.right : parent.left
+ anchors.right: parent.right
anchors.leftMargin: UM.Theme.getSize("thin_margin").width
anchors.verticalCenter: icon.verticalCenter
text: "Empty label"
+ elide: Text.ElideRight
color: UM.Theme.getColor("text")
font: UM.Theme.getFont("very_small")
renderType: Text.NativeRendering
diff --git a/resources/qml/MachineSelector.qml b/resources/qml/MachineSelector.qml
deleted file mode 100644
index 750ac7f620..0000000000
--- a/resources/qml/MachineSelector.qml
+++ /dev/null
@@ -1,140 +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 2.3
-import QtQuick.Controls.Styles 1.1
-import QtQuick.Layouts 1.1
-
-import UM 1.2 as UM
-import Cura 1.0 as Cura
-import "Menus"
-
-
-Cura.ExpandableComponent
-{
- id: machineSelector
-
- property bool isNetworkPrinter: Cura.MachineManager.activeMachineNetworkKey != ""
-
- iconSource: expanded ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left")
-
- UM.I18nCatalog
- {
- id: catalog
- name: "cura"
- }
-
- headerItem: Label
- {
- text: isNetworkPrinter ? Cura.MachineManager.activeMachineNetworkGroupName : Cura.MachineManager.activeMachineName
- verticalAlignment: Text.AlignVCenter
- height: parent.height
- elide: Text.ElideRight
- font: UM.Theme.getFont("default")
- color: UM.Theme.getColor("text")
- renderType: Text.NativeRendering
- }
-
- popupItem: Item
- {
- id: popup
- width: machineSelector.width - 2 * UM.Theme.getSize("default_margin").width
- height: 200
-
- ScrollView
- {
- anchors.fill: parent
- contentHeight: column.implicitHeight
- contentWidth: column.implicitWidth
- clip: true
- ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
-
- Column
- {
- id: column
- anchors.fill: parent
- Label
- {
- text: catalog.i18nc("@label", "Networked Printers")
- visible: networkedPrintersModel.items.length > 0
- height: visible ? contentHeight + 2 * UM.Theme.getSize("default_margin").height : 0
- font: UM.Theme.getFont("medium_bold")
- color: UM.Theme.getColor("text")
- renderType: Text.NativeRendering
- verticalAlignment: Text.AlignVCenter
- }
-
- Repeater
- {
- id: networkedPrinters
-
- model: UM.ContainerStacksModel
- {
- id: networkedPrintersModel
- filter: {"type": "machine", "um_network_key": "*", "hidden": "False"}
- }
-
- delegate: RoundButton
- {
- text: name
- width: parent.width
-
- checkable: true
- radius: UM.Theme.getSize("default_radius").width
- onClicked:
- {
- togglePopup()
- Cura.MachineManager.setActiveMachine(model.id)
- }
-
- Connections
- {
- target: Cura.MachineManager
- onActiveMachineNetworkGroupNameChanged: checked = Cura.MachineManager.activeMachineNetworkGroupName == model.metadata["connect_group_name"]
- }
- }
-
- }
-
- Label
- {
- text: catalog.i18nc("@label", "Virtual Printers")
- visible: virtualPrintersModel.items.length > 0
- height: visible ? contentHeight + 2 * UM.Theme.getSize("default_margin").height : 0
- font: UM.Theme.getFont("medium_bold")
- color: UM.Theme.getColor("text")
- verticalAlignment: Text.AlignVCenter
- renderType: Text.NativeRendering
- }
-
- Repeater
- {
- id: virtualPrinters
-
- model: UM.ContainerStacksModel
- {
- id: virtualPrintersModel
- filter: {"type": "machine", "um_network_key": null}
- }
-
- delegate: RoundButton
- {
- text: name
- width: parent.width
- checked: Cura.MachineManager.activeMachineId == model.id
- checkable: true
-
- radius: UM.Theme.getSize("default_radius").width
- onClicked:
- {
- togglePopup()
- Cura.MachineManager.setActiveMachine(model.id)
- }
- }
-
- }
- }
- }
- }
-}
diff --git a/resources/qml/MainWindow/MainWindowHeader.qml b/resources/qml/MainWindow/MainWindowHeader.qml
index 59ec542e6b..34936e9b5a 100644
--- a/resources/qml/MainWindow/MainWindowHeader.qml
+++ b/resources/qml/MainWindow/MainWindowHeader.qml
@@ -2,11 +2,13 @@
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.7
+import QtQuick.Controls 2.0 as Controls2
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.1
import UM 1.4 as UM
import Cura 1.0 as Cura
+import QtGraphicalEffects 1.0
import "../Account"
@@ -16,7 +18,31 @@ Rectangle
implicitHeight: UM.Theme.getSize("main_window_header").height
implicitWidth: UM.Theme.getSize("main_window_header").width
- color: UM.Theme.getColor("main_window_header_background")
+
+ LinearGradient
+ {
+ 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")
+ }
+ }
+ }
Image
{
@@ -73,25 +99,39 @@ Rectangle
}
// Shortcut button to quick access the Toolbox
- Cura.ActionButton
+ Controls2.Button
{
+ id: marketplaceButton
+ text: catalog.i18nc("@action:button", "Marketplace")
+ height: Math.round(0.5 * UM.Theme.getSize("main_window_header").height)
+ onClicked: Cura.Actions.browsePackages.trigger()
+
+ hoverEnabled: true
+
+ background: Rectangle
+ {
+ radius: UM.Theme.getSize("action_button_radius").width
+ color: marketplaceButton.hovered ? UM.Theme.getColor("primary_text") : UM.Theme.getColor("main_window_header_background")
+ border.width: UM.Theme.getSize("default_lining").width
+ border.color: UM.Theme.getColor("primary_text")
+ }
+
+ contentItem: Label
+ {
+ id: label
+ text: marketplaceButton.text
+ color: marketplaceButton.hovered ? UM.Theme.getColor("main_window_header_background") : UM.Theme.getColor("primary_text")
+ width: contentWidth
+ verticalAlignment: Text.AlignVCenter
+ renderType: Text.NativeRendering
+ }
+
anchors
{
right: accountWidget.left
rightMargin: UM.Theme.getSize("default_margin").width
verticalCenter: parent.verticalCenter
}
- leftPadding: UM.Theme.getSize("default_margin").width
- rightPadding: UM.Theme.getSize("default_margin").width
- text: catalog.i18nc("@action:button", "Marketplace")
- height: Math.round(0.5 * UM.Theme.getSize("main_window_header").height)
- color: UM.Theme.getColor("main_window_header_secondary_button_background_active")
- hoverColor: UM.Theme.getColor("main_window_header_secondary_button_background_hovered")
- outlineColor: UM.Theme.getColor("main_window_header_secondary_button_outline_active")
- outlineHoverColor: UM.Theme.getColor("main_window_header_secondary_button_outline_hovered")
- textColor: UM.Theme.getColor("main_window_header_secondary_button_text_active")
- textHoverColor: UM.Theme.getColor("main_window_header_secondary_button_text_hovered")
- onClicked: Cura.Actions.browsePackages.trigger()
}
AccountWidget
diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml
index 7aaf87b4df..210ff6057f 100644
--- a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml
+++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml
@@ -21,7 +21,7 @@ Column
{
// FIXME For now the model should be removed and then created again, otherwise changes in the printer don't automatically update the UI
configurationList.model = []
- if(outputDevice)
+ if (outputDevice)
{
configurationList.model = outputDevice.uniqueConfigurations
}
diff --git a/resources/qml/Menus/PrinterStatusIcon.qml b/resources/qml/Menus/PrinterStatusIcon.qml
deleted file mode 100644
index 6ff6b07af8..0000000000
--- a/resources/qml/Menus/PrinterStatusIcon.qml
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (c) 2017 Ultimaker B.V.
-// Cura is released under the terms of the LGPLv3 or higher.
-
-import QtQuick 2.2
-
-import UM 1.2 as UM
-import Cura 1.0 as Cura
-
-Item
-{
- property var status: "disconnected"
- width: childrenRect.width
- height: childrenRect.height
- UM.RecolorImage
- {
- id: statusIcon
- width: UM.Theme.getSize("printer_status_icon").width
- height: UM.Theme.getSize("printer_status_icon").height
- sourceSize.width: width
- sourceSize.height: width
- color: UM.Theme.getColor("tab_status_" + parent.status)
- source: UM.Theme.getIcon(parent.status)
- }
-}
-
-
-
diff --git a/resources/qml/PrimaryButton.qml b/resources/qml/PrimaryButton.qml
new file mode 100644
index 0000000000..fca63d2cdb
--- /dev/null
+++ b/resources/qml/PrimaryButton.qml
@@ -0,0 +1,20 @@
+// Copyright (c) 2018 Ultimaker B.V.
+// Cura is released under the terms of the LGPLv3 or higher.
+
+import QtQuick 2.2
+
+import UM 1.4 as UM
+import Cura 1.1 as Cura
+
+
+Cura.ActionButton
+{
+ shadowEnabled: true
+ shadowColor: enabled ? UM.Theme.getColor("primary_button_shadow"): UM.Theme.getColor("action_button_disabled_shadow")
+ color: UM.Theme.getColor("primary_button")
+ textColor: UM.Theme.getColor("primary_button_text")
+ outlineColor: "transparent"
+ disabledColor: UM.Theme.getColor("action_button_disabled")
+ textDisabledColor: UM.Theme.getColor("action_button_disabled_text")
+ hoverColor: UM.Theme.getColor("primary_button_hover")
+}
\ No newline at end of file
diff --git a/resources/qml/PrintSetupSelector.qml b/resources/qml/PrintSetupSelector.qml
index 6edb297721..253a13c0f0 100644
--- a/resources/qml/PrintSetupSelector.qml
+++ b/resources/qml/PrintSetupSelector.qml
@@ -64,12 +64,12 @@ Cura.ExpandableComponent
IconWithText
{
source: UM.Theme.getIcon("category_layer_height")
- text: Cura.MachineManager.activeQualityOrQualityChangesName + " " + layerHeight.properties.value + "mm"
+ text: Cura.MachineManager.activeStack ? Cura.MachineManager.activeQualityOrQualityChangesName + " " + layerHeight.properties.value + "mm" : ""
UM.SettingPropertyProvider
{
id: layerHeight
- containerStackId: Cura.MachineManager.activeStackId
+ containerStack: Cura.MachineManager.activeStack
key: "layer_height"
watchedProperties: ["value"]
}
@@ -78,12 +78,12 @@ Cura.ExpandableComponent
IconWithText
{
source: UM.Theme.getIcon("category_infill")
- text: parseInt(infillDensity.properties.value) + "%"
+ text: Cura.MachineManager.activeStack ? parseInt(infillDensity.properties.value) + "%" : "0%"
UM.SettingPropertyProvider
{
id: infillDensity
- containerStackId: Cura.MachineManager.activeStackId
+ containerStack: Cura.MachineManager.activeStack
key: "infill_sparse_density"
watchedProperties: ["value"]
}
diff --git a/resources/qml/PrinterSelector/MachineSelector.qml b/resources/qml/PrinterSelector/MachineSelector.qml
new file mode 100644
index 0000000000..15cd773c90
--- /dev/null
+++ b/resources/qml/PrinterSelector/MachineSelector.qml
@@ -0,0 +1,155 @@
+// 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
+
+Cura.ExpandableComponent
+{
+ id: machineSelector
+
+ property bool isNetworkPrinter: Cura.MachineManager.activeMachineNetworkKey != ""
+ property bool isPrinterConnected: Cura.MachineManager.printerConnected
+ property var outputDevice: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null
+
+ popupPadding: UM.Theme.getSize("default_lining").width
+ popupAlignment: Cura.ExpandableComponent.PopupAlignment.AlignLeft
+ iconSource: expanded ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left")
+
+ UM.I18nCatalog
+ {
+ id: catalog
+ name: "cura"
+ }
+
+ headerItem: Cura.IconLabel
+ {
+ text: isNetworkPrinter ? Cura.MachineManager.activeMachineNetworkGroupName : Cura.MachineManager.activeMachineName
+ source:
+ {
+ if (isNetworkPrinter)
+ {
+ if (machineSelector.outputDevice != null && machineSelector.outputDevice.clusterSize > 1)
+ {
+ return UM.Theme.getIcon("printer_group")
+ }
+ return UM.Theme.getIcon("printer_single")
+ }
+ return ""
+ }
+ font: UM.Theme.getFont("medium")
+ color: UM.Theme.getColor("text")
+ iconSize: UM.Theme.getSize("machine_selector_icon").width
+
+ UM.RecolorImage
+ {
+ id: icon
+
+ anchors
+ {
+ bottom: parent.bottom
+ left: parent.left
+ leftMargin: UM.Theme.getSize("thick_margin").width
+ }
+
+ source: UM.Theme.getIcon("printer_connected")
+ width: UM.Theme.getSize("printer_status_icon").width
+ height: UM.Theme.getSize("printer_status_icon").height
+
+ sourceSize.width: width
+ sourceSize.height: height
+
+ color: UM.Theme.getColor("primary")
+ visible: isNetworkPrinter && isPrinterConnected
+
+ // Make a themable circle in the background so we can change it in other themes
+ Rectangle
+ {
+ id: iconBackground
+ anchors.centerIn: parent
+ // Make it a bit bigger so there is an outline
+ width: parent.width + 2 * UM.Theme.getSize("default_lining").width
+ height: parent.height + 2 * UM.Theme.getSize("default_lining").height
+ radius: Math.round(width / 2)
+ color: UM.Theme.getColor("main_background")
+ z: parent.z - 1
+ }
+ }
+ }
+
+ popupItem: Item
+ {
+ id: popup
+ width: UM.Theme.getSize("machine_selector_widget_content").width
+
+ ScrollView
+ {
+ id: scroll
+ width: parent.width
+ clip: true
+ leftPadding: UM.Theme.getSize("default_lining").width
+ rightPadding: UM.Theme.getSize("default_lining").width
+
+ MachineSelectorList
+ {
+ // Can't use parent.width since the parent is the flickable component and not the ScrollView
+ width: scroll.width - scroll.leftPadding - scroll.rightPadding
+ property real maximumHeight: UM.Theme.getSize("machine_selector_widget_content").height - buttonRow.height
+
+ onHeightChanged:
+ {
+ scroll.height = Math.min(height, maximumHeight)
+ popup.height = scroll.height + buttonRow.height
+ }
+ }
+ }
+
+ Rectangle
+ {
+ id: separator
+
+ anchors.top: scroll.bottom
+ width: parent.width
+ height: UM.Theme.getSize("default_lining").height
+ color: UM.Theme.getColor("lining")
+ }
+
+ Row
+ {
+ id: buttonRow
+
+ // The separator is inside the buttonRow. This is to avoid some weird behaviours with the scroll bar.
+ anchors.top: separator.top
+ anchors.horizontalCenter: parent.horizontalCenter
+ padding: UM.Theme.getSize("default_margin").width
+ spacing: UM.Theme.getSize("default_margin").width
+
+ Cura.SecondaryButton
+ {
+ leftPadding: UM.Theme.getSize("default_margin").width
+ rightPadding: UM.Theme.getSize("default_margin").width
+ text: catalog.i18nc("@button", "Add printer")
+ onClicked:
+ {
+ togglePopup()
+ Cura.Actions.addMachine.trigger()
+ }
+ }
+
+ Cura.SecondaryButton
+ {
+ leftPadding: UM.Theme.getSize("default_margin").width
+ rightPadding: UM.Theme.getSize("default_margin").width
+ text: catalog.i18nc("@button", "Manage printers")
+ onClicked:
+ {
+ togglePopup()
+ Cura.Actions.configureMachines.trigger()
+ }
+ }
+ }
+ }
+}
diff --git a/resources/qml/PrinterSelector/MachineSelectorButton.qml b/resources/qml/PrinterSelector/MachineSelectorButton.qml
new file mode 100644
index 0000000000..369e75cede
--- /dev/null
+++ b/resources/qml/PrinterSelector/MachineSelectorButton.qml
@@ -0,0 +1,103 @@
+// 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
+
+Button
+{
+ id: machineSelectorButton
+
+ width: parent.width
+ height: UM.Theme.getSize("action_button").height
+ leftPadding: UM.Theme.getSize("thick_margin").width
+ rightPadding: UM.Theme.getSize("thick_margin").width
+ checkable: true
+ hoverEnabled: true
+
+ property var outputDevice: null
+ property var printerTypesList: []
+
+ function updatePrinterTypesList()
+ {
+ printerTypesList = (checked && (outputDevice != null)) ? outputDevice.uniquePrinterTypes : []
+ }
+
+ contentItem: Item
+ {
+ width: machineSelectorButton.width - machineSelectorButton.leftPadding
+ height: UM.Theme.getSize("action_button").height
+
+ Label
+ {
+ id: buttonText
+ anchors
+ {
+ left: parent.left
+ right: printerTypes.left
+ verticalCenter: parent.verticalCenter
+ }
+ text: machineSelectorButton.text
+ color: UM.Theme.getColor("text")
+ font: UM.Theme.getFont("action_button")
+ visible: text != ""
+ renderType: Text.NativeRendering
+ verticalAlignment: Text.AlignVCenter
+ elide: Text.ElideRight
+ }
+
+ Row
+ {
+ id: printerTypes
+ width: childrenRect.width
+
+ anchors
+ {
+ right: parent.right
+ verticalCenter: parent.verticalCenter
+ }
+ spacing: UM.Theme.getSize("narrow_margin").width
+
+ Repeater
+ {
+ model: printerTypesList
+ delegate: Cura.PrinterTypeLabel
+ {
+ text: Cura.MachineManager.getAbbreviatedMachineName(modelData)
+ }
+ }
+ }
+ }
+
+ background: Rectangle
+ {
+ id: backgroundRect
+ color: machineSelectorButton.hovered ? UM.Theme.getColor("action_button_hovered") : "transparent"
+ radius: UM.Theme.getSize("action_button_radius").width
+ border.width: UM.Theme.getSize("default_lining").width
+ border.color: machineSelectorButton.checked ? UM.Theme.getColor("primary") : "transparent"
+ }
+
+ onClicked:
+ {
+ togglePopup()
+ Cura.MachineManager.setActiveMachine(model.id)
+ }
+
+ Connections
+ {
+ target: outputDevice
+ onUniqueConfigurationsChanged: updatePrinterTypesList()
+ }
+
+ Connections
+ {
+ target: Cura.MachineManager
+ onOutputDevicesChanged: updatePrinterTypesList()
+ }
+
+ Component.onCompleted: updatePrinterTypesList()
+}
diff --git a/resources/qml/PrinterSelector/MachineSelectorList.qml b/resources/qml/PrinterSelector/MachineSelectorList.qml
new file mode 100644
index 0000000000..5ef04b7351
--- /dev/null
+++ b/resources/qml/PrinterSelector/MachineSelectorList.qml
@@ -0,0 +1,84 @@
+// 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
+
+Column
+{
+ id: machineSelectorList
+
+ Label
+ {
+ text: catalog.i18nc("@label", "Network connected printers")
+ visible: networkedPrintersModel.items.length > 0
+ leftPadding: UM.Theme.getSize("default_margin").width
+ height: visible ? contentHeight + 2 * UM.Theme.getSize("default_margin").height : 0
+ renderType: Text.NativeRendering
+ font: UM.Theme.getFont("medium")
+ color: UM.Theme.getColor("text_medium")
+ verticalAlignment: Text.AlignVCenter
+ }
+
+ Repeater
+ {
+ id: networkedPrinters
+
+ model: UM.ContainerStacksModel
+ {
+ id: networkedPrintersModel
+ filter:
+ {
+ "type": "machine", "um_network_key": "*", "hidden": "False"
+ }
+ }
+
+ delegate: MachineSelectorButton
+ {
+ text: model.metadata["connect_group_name"]
+ checked: Cura.MachineManager.activeMachineNetworkGroupName == model.metadata["connect_group_name"]
+ outputDevice: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null
+
+ Connections
+ {
+ target: Cura.MachineManager
+ onActiveMachineNetworkGroupNameChanged: checked = Cura.MachineManager.activeMachineNetworkGroupName == model.metadata["connect_group_name"]
+ }
+ }
+ }
+
+ Label
+ {
+ text: catalog.i18nc("@label", "Preset printers")
+ visible: virtualPrintersModel.items.length > 0
+ leftPadding: UM.Theme.getSize("default_margin").width
+ height: visible ? contentHeight + 2 * UM.Theme.getSize("default_margin").height : 0
+ renderType: Text.NativeRendering
+ font: UM.Theme.getFont("medium")
+ color: UM.Theme.getColor("text_medium")
+ verticalAlignment: Text.AlignVCenter
+ }
+
+ Repeater
+ {
+ id: virtualPrinters
+
+ model: UM.ContainerStacksModel
+ {
+ id: virtualPrintersModel
+ filter:
+ {
+ "type": "machine", "um_network_key": null
+ }
+ }
+
+ delegate: MachineSelectorButton
+ {
+ text: model.name
+ checked: Cura.MachineManager.activeMachineId == model.id
+ }
+ }
+}
\ No newline at end of file
diff --git a/resources/qml/PrinterSelector/PrinterTypeLabel.qml b/resources/qml/PrinterSelector/PrinterTypeLabel.qml
new file mode 100644
index 0000000000..cd9f3b9743
--- /dev/null
+++ b/resources/qml/PrinterSelector/PrinterTypeLabel.qml
@@ -0,0 +1,34 @@
+// 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
+
+// This component creates a label with the abbreviated name of a printer, with a rectangle surrounding the label.
+// It is created in a separated place in order to be reused whenever needed.
+Item
+{
+ property alias text: printerTypeLabel.text
+
+ width: UM.Theme.getSize("printer_type_label").width
+ height: UM.Theme.getSize("printer_type_label").height
+
+ Rectangle
+ {
+ anchors.fill: parent
+ color: UM.Theme.getColor("printer_type_label_background")
+ }
+
+ Label
+ {
+ id: printerTypeLabel
+ text: "CFFFP" // As an abbreviated name of the Custom FFF Printer
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.horizontalCenter: parent.horizontalCenter
+ renderType: Text.NativeRendering
+ font: UM.Theme.getFont("very_small")
+ color: UM.Theme.getColor("text")
+ }
+}
\ No newline at end of file
diff --git a/resources/qml/RoundedRectangle.qml b/resources/qml/RoundedRectangle.qml
index 9ad2230be5..3ca05e2125 100644
--- a/resources/qml/RoundedRectangle.qml
+++ b/resources/qml/RoundedRectangle.qml
@@ -5,6 +5,7 @@ import UM 1.2 as UM
// The rounded rectangle works mostly like a regular rectangle, but provides the option to have rounded corners on only one side of the rectangle.
Item
{
+ id: roundedRectangle
// As per the regular rectangle
property color color: "transparent"
@@ -15,6 +16,9 @@ Item
// 1 is down, 2 is left, 3 is up and 4 is right.
property int cornerSide: RoundedRectangle.Direction.None
+ // Simple object to ensure that border.width and border.color work
+ property BorderGroup border: BorderGroup {}
+
enum Direction
{
None = 0,
@@ -31,6 +35,8 @@ Item
anchors.fill: parent
radius: cornerSide != RoundedRectangle.Direction.None ? parent.radius : 0
color: parent.color
+ border.width: parent.border.width
+ border.color: parent.border.color
}
// The item that covers 2 of the corners to make them not rounded.
@@ -45,5 +51,22 @@ Item
right: cornerSide == RoundedRectangle.Direction.Left ? parent.right: undefined
bottom: cornerSide == RoundedRectangle.Direction.Up ? parent.bottom: undefined
}
+
+ border.width: parent.border.width
+ border.color: parent.border.color
+
+ Rectangle
+ {
+ color: roundedRectangle.color
+ height: cornerSide % 2 ? roundedRectangle.border.width: roundedRectangle.height - 2 * roundedRectangle.border.width
+ width: cornerSide % 2 ? roundedRectangle.width - 2 * roundedRectangle.border.width: roundedRectangle.border.width
+ anchors
+ {
+ right: cornerSide == RoundedRectangle.Direction.Right ? parent.right : undefined
+ bottom: cornerSide == RoundedRectangle.Direction.Down ? parent.bottom: undefined
+ horizontalCenter: cornerSide % 2 ? parent.horizontalCenter: undefined
+ verticalCenter: cornerSide % 2 ? undefined: parent.verticalCenter
+ }
+ }
}
}
diff --git a/resources/qml/SaveButton.qml b/resources/qml/SaveButton.qml
deleted file mode 100644
index c2d310e30c..0000000000
--- a/resources/qml/SaveButton.qml
+++ /dev/null
@@ -1,478 +0,0 @@
-// 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 QtQuick.Controls.Styles 1.1
-import QtQuick.Layouts 1.1
-
-import UM 1.1 as UM
-import Cura 1.0 as Cura
-
-// This widget does so much more than "just" being a save button, so it should be refactored at some point in time.
-Item
-{
- id: base;
- UM.I18nCatalog { id: catalog; name: "cura"}
-
- property real progress: UM.Backend.progress
- property int backendState: UM.Backend.state
- property bool activity: CuraApplication.platformActivity
-
- property alias buttonRowWidth: saveRow.width
-
- property string fileBaseName
- property string statusText:
- {
- if(!activity)
- {
- return catalog.i18nc("@label:PrintjobStatus", "Please load a 3D model");
- }
-
- switch(base.backendState)
- {
- case 1:
- return catalog.i18nc("@label:PrintjobStatus", "Ready to slice");
- case 2:
- return catalog.i18nc("@label:PrintjobStatus", "Slicing...");
- case 3:
- return catalog.i18nc("@label:PrintjobStatus %1 is target operation", "Ready to %1").arg(UM.OutputDeviceManager.activeDeviceShortDescription);
- case 4:
- return catalog.i18nc("@label:PrintjobStatus", "Unable to Slice");
- case 5:
- return catalog.i18nc("@label:PrintjobStatus", "Slicing unavailable");
- default:
- return "";
- }
- }
-
- function sliceOrStopSlicing()
- {
- try
- {
- if ([1, 5].indexOf(base.backendState) != -1)
- {
- CuraApplication.backend.forceSlice();
- }
- else
- {
- CuraApplication.backend.stopSlicing();
- }
- }
- catch (e)
- {
- console.log("Could not start or stop slicing.", e)
- }
- }
-
- Label
- {
- id: statusLabel
- width: parent.width - 2 * UM.Theme.getSize("thick_margin").width
- anchors.top: parent.top
- anchors.left: parent.left
- anchors.leftMargin: UM.Theme.getSize("thick_margin").width
-
- color: UM.Theme.getColor("text")
- font: UM.Theme.getFont("default_bold")
- text: statusText;
- }
-
- Rectangle
- {
- id: progressBar
- width: parent.width - 2 * UM.Theme.getSize("thick_margin").width
- height: UM.Theme.getSize("progressbar").height
- anchors.top: statusLabel.bottom
- anchors.topMargin: Math.round(UM.Theme.getSize("thick_margin").height / 4)
- anchors.left: parent.left
- anchors.leftMargin: UM.Theme.getSize("thick_margin").width
- radius: UM.Theme.getSize("progressbar_radius").width
- color: UM.Theme.getColor("progressbar_background")
-
- Rectangle
- {
- width: Math.max(parent.width * base.progress)
- height: parent.height
- color: UM.Theme.getColor("progressbar_control")
- radius: UM.Theme.getSize("progressbar_radius").width
- visible: base.backendState == 2
- }
- }
-
- // Shortcut for "save as/print/..."
- Action
- {
- shortcut: "Ctrl+P"
- onTriggered:
- {
- // only work when the button is enabled
- if (saveToButton.enabled)
- {
- saveToButton.clicked();
- }
- // prepare button
- if (prepareButton.enabled)
- {
- sliceOrStopSlicing();
- }
- }
- }
-
- Item
- {
- id: saveRow
- width: {
- // using childrenRect.width directly causes a binding loop, because setting the width affects the childrenRect
- var children_width = UM.Theme.getSize("default_margin").width;
- for (var index in children)
- {
- var child = children[index];
- if(child.visible)
- {
- children_width += child.width + child.anchors.rightMargin;
- }
- }
- return Math.min(children_width, base.width - UM.Theme.getSize("thick_margin").width);
- }
- height: saveToButton.height
- anchors.bottom: parent.bottom
- anchors.bottomMargin: UM.Theme.getSize("thick_margin").height
- anchors.right: parent.right
- clip: true
-
- Row
- {
- id: additionalComponentsRow
- anchors.top: parent.top
- anchors.right: saveToButton.visible ? saveToButton.left : (prepareButton.visible ? prepareButton.left : parent.right)
- anchors.rightMargin: UM.Theme.getSize("default_margin").width
-
- spacing: UM.Theme.getSize("default_margin").width
- }
-
- Component.onCompleted:
- {
- saveRow.addAdditionalComponents("saveButton")
- }
-
- Connections
- {
- target: CuraApplication
- onAdditionalComponentsChanged: saveRow.addAdditionalComponents("saveButton")
- }
-
- function addAdditionalComponents (areaId)
- {
- if(areaId == "saveButton")
- {
- for (var component in CuraApplication.additionalComponents["saveButton"])
- {
- CuraApplication.additionalComponents["saveButton"][component].parent = additionalComponentsRow
- }
- }
- }
-
- Connections
- {
- target: UM.Preferences
- onPreferenceChanged:
- {
- var autoSlice = UM.Preferences.getValue("general/auto_slice");
- prepareButton.autoSlice = autoSlice;
- saveToButton.autoSlice = autoSlice;
- }
- }
-
- // Prepare button, only shows if auto_slice is off
- Button
- {
- id: prepareButton
-
- tooltip: [1, 5].indexOf(base.backendState) != -1 ? catalog.i18nc("@info:tooltip","Slice current printjob") : catalog.i18nc("@info:tooltip","Cancel slicing process")
- // 1 = not started, 2 = Processing
- enabled: ([1, 2].indexOf(base.backendState) != -1) && base.activity
- visible: !autoSlice && ([1, 2, 4].indexOf(base.backendState) != -1) && base.activity
- property bool autoSlice
- height: UM.Theme.getSize("save_button_save_to_button").height
-
- anchors.top: parent.top
- anchors.right: parent.right
- anchors.rightMargin: UM.Theme.getSize("thick_margin").width
-
- // 1 = not started, 4 = error, 5 = disabled
- text: [1, 4, 5].indexOf(base.backendState) != -1 ? catalog.i18nc("@label:Printjob", "Prepare") : catalog.i18nc("@label:Printjob", "Cancel")
- onClicked:
- {
- sliceOrStopSlicing();
- }
-
- style: ButtonStyle
- {
- background: Rectangle
- {
- border.width: UM.Theme.getSize("default_lining").width
- border.color:
- {
- if(!control.enabled)
- {
- return UM.Theme.getColor("action_button_disabled_border");
- }
- else if(control.pressed)
- {
- return UM.Theme.getColor("action_button_active_border");
- }
- else if(control.hovered)
- {
- return UM.Theme.getColor("action_button_hovered_border");
- }
- else
- {
- return UM.Theme.getColor("action_button_border");
- }
- }
- color:
- {
- if(!control.enabled)
- {
- return UM.Theme.getColor("action_button_disabled");
- }
- else if(control.pressed)
- {
- return UM.Theme.getColor("action_button_active");
- }
- else if(control.hovered)
- {
- return UM.Theme.getColor("action_button_hovered");
- }
- else
- {
- return UM.Theme.getColor("action_button");
- }
- }
-
- Behavior on color { ColorAnimation { duration: 50; } }
-
- implicitWidth: actualLabel.contentWidth + (UM.Theme.getSize("thick_margin").width * 2)
-
- Label
- {
- id: actualLabel
- anchors.centerIn: parent
- color:
- {
- if(!control.enabled)
- {
- return UM.Theme.getColor("action_button_disabled_text");
- }
- else if(control.pressed)
- {
- return UM.Theme.getColor("action_button_active_text");
- }
- else if(control.hovered)
- {
- return UM.Theme.getColor("action_button_hovered_text");
- }
- else
- {
- return UM.Theme.getColor("action_button_text");
- }
- }
- font: UM.Theme.getFont("action_button")
- text: control.text;
- }
- }
- label: Item {}
- }
- }
-
- Button
- {
- id: saveToButton
-
- tooltip: UM.OutputDeviceManager.activeDeviceDescription;
- // 3 = done, 5 = disabled
- enabled: base.backendState != "undefined" && (base.backendState == 3 || base.backendState == 5) && base.activity == true
- visible: base.backendState != "undefined" && autoSlice || ((base.backendState == 3 || base.backendState == 5) && base.activity == true)
- property bool autoSlice
- height: UM.Theme.getSize("save_button_save_to_button").height
-
- anchors.top: parent.top
- anchors.right: deviceSelectionMenu.visible ? deviceSelectionMenu.left : parent.right
- anchors.rightMargin: deviceSelectionMenu.visible ? -3 * UM.Theme.getSize("default_lining").width : UM.Theme.getSize("thick_margin").width
-
- 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 });
- }
-
- style: ButtonStyle
- {
- background: Rectangle
- {
- border.width: UM.Theme.getSize("default_lining").width
- border.color:
- {
- if(!control.enabled)
- {
- return UM.Theme.getColor("action_button_disabled_border");
- }
- else if(control.pressed)
- {
- return UM.Theme.getColor("print_button_ready_pressed_border");
- }
- else if(control.hovered)
- {
- return UM.Theme.getColor("print_button_ready_hovered_border");
- }
- else
- {
- return UM.Theme.getColor("print_button_ready_border");
- }
- }
- color:
- {
- if(!control.enabled)
- {
- return UM.Theme.getColor("action_button_disabled");
- }
- else if(control.pressed)
- {
- return UM.Theme.getColor("print_button_ready_pressed");
- }
- else if(control.hovered)
- {
- return UM.Theme.getColor("print_button_ready_hovered");
- }
- else
- {
- return UM.Theme.getColor("print_button_ready");
- }
- }
-
- Behavior on color { ColorAnimation { duration: 50; } }
-
- implicitWidth: actualLabel.contentWidth + (UM.Theme.getSize("thick_margin").width * 2)
-
- Label
- {
- id: actualLabel
- anchors.centerIn: parent
- color: control.enabled ? UM.Theme.getColor("print_button_ready_text") : UM.Theme.getColor("action_button_disabled_text")
- font: UM.Theme.getFont("action_button")
- text: control.text
- }
- }
- label: Item { }
- }
- }
-
- Button
- {
- id: deviceSelectionMenu
- tooltip: catalog.i18nc("@info:tooltip","Select the active output device");
- anchors.top: parent.top
- anchors.right: parent.right
-
- anchors.rightMargin: UM.Theme.getSize("thick_margin").width
- width: UM.Theme.getSize("save_button_save_to_button").height
- height: UM.Theme.getSize("save_button_save_to_button").height
-
- // 3 = Done, 5 = Disabled
- enabled: (base.backendState == 3 || base.backendState == 5) && base.activity == true
- visible: (devicesModel.deviceCount > 1) && (base.backendState == 3 || base.backendState == 5) && base.activity == true
-
-
- style: ButtonStyle
- {
- background: Rectangle
- {
- id: deviceSelectionIcon
- border.width: UM.Theme.getSize("default_lining").width
- border.color:
- {
- if(!control.enabled)
- {
- return UM.Theme.getColor("action_button_disabled_border")
- }
- else if(control.pressed)
- {
- return UM.Theme.getColor("print_button_ready_pressed_border")
- }
- else if(control.hovered)
- {
- return UM.Theme.getColor("print_button_ready_hovered_border")
- }
- else
- {
- return UM.Theme.getColor("print_button_ready_border")
- }
- }
- color:
- {
- if(!control.enabled)
- {
- return UM.Theme.getColor("action_button_disabled")
- }
- else if(control.pressed)
- {
- return UM.Theme.getColor("print_button_ready_pressed")
- }
- else if(control.hovered)
- {
- return UM.Theme.getColor("print_button_ready_hovered")
- }
- else
- {
- return UM.Theme.getColor("print_button_ready")
- }
- }
- Behavior on color { ColorAnimation { duration: 50; } }
- anchors.left: parent.left
- anchors.leftMargin: Math.round(UM.Theme.getSize("save_button_text_margin").width / 2);
- width: parent.height
- height: parent.height
-
- UM.RecolorImage
- {
- anchors.verticalCenter: parent.verticalCenter
- anchors.horizontalCenter: parent.horizontalCenter
- width: UM.Theme.getSize("standard_arrow").width
- height: UM.Theme.getSize("standard_arrow").height
- sourceSize.width: width
- sourceSize.height: height
- color: control.enabled ? UM.Theme.getColor("print_button_ready_text") : UM.Theme.getColor("action_button_disabled_text")
- source: UM.Theme.getIcon("arrow_bottom")
- }
- }
- }
-
- menu: Menu
- {
- id: devicesMenu;
- Instantiator
- {
- model: devicesModel;
- MenuItem
- {
- text: model.description
- checkable: true;
- checked: model.id == UM.OutputDeviceManager.activeDevice
- exclusiveGroup: devicesMenuGroup
- onTriggered:
- {
- UM.OutputDeviceManager.setActiveDevice(model.id);
- }
- }
- onObjectAdded: devicesMenu.insertItem(index, object)
- onObjectRemoved: devicesMenu.removeItem(object)
- }
- ExclusiveGroup { id: devicesMenuGroup }
- }
- }
- UM.OutputDevicesModel { id: devicesModel }
- }
-}
diff --git a/resources/qml/SecondaryButton.qml b/resources/qml/SecondaryButton.qml
new file mode 100644
index 0000000000..f03d8acdfa
--- /dev/null
+++ b/resources/qml/SecondaryButton.qml
@@ -0,0 +1,20 @@
+// Copyright (c) 2018 Ultimaker B.V.
+// Cura is released under the terms of the LGPLv3 or higher.
+
+import QtQuick 2.2
+
+import UM 1.4 as UM
+import Cura 1.1 as Cura
+
+
+Cura.ActionButton
+{
+ shadowEnabled: true
+ shadowColor: enabled ? UM.Theme.getColor("secondary_button_shadow"): UM.Theme.getColor("action_button_disabled_shadow")
+ color: UM.Theme.getColor("secondary_button")
+ textColor: UM.Theme.getColor("secondary_button_text")
+ outlineColor: "transparent"
+ disabledColor: UM.Theme.getColor("action_button_disabled")
+ textDisabledColor: UM.Theme.getColor("action_button_disabled_text")
+ hoverColor: UM.Theme.getColor("secondary_button_hover")
+}
\ No newline at end of file
diff --git a/resources/qml/Settings/SettingCategory.qml b/resources/qml/Settings/SettingCategory.qml
index 7995619af0..aafe36c546 100644
--- a/resources/qml/Settings/SettingCategory.qml
+++ b/resources/qml/Settings/SettingCategory.qml
@@ -14,23 +14,32 @@ Button
anchors.right: parent.right
anchors.leftMargin: UM.Theme.getSize("thick_margin").width
anchors.rightMargin: UM.Theme.getSize("thick_margin").width
+ hoverEnabled: true
+
background: Rectangle
{
id: backgroundRectangle
implicitHeight: UM.Theme.getSize("section").height
- color: {
- if (base.color) {
- return base.color;
- } else if (!base.enabled) {
- return UM.Theme.getColor("setting_category_disabled");
- } else if (base.hovered && base.checkable && base.checked) {
- return UM.Theme.getColor("setting_category_active_hover");
- } else if (base.pressed || (base.checkable && base.checked)) {
- return UM.Theme.getColor("setting_category_active");
- } else if (base.hovered) {
- return UM.Theme.getColor("setting_category_hover");
- } else {
- return UM.Theme.getColor("setting_category");
+ color:
+ {
+ if (base.color)
+ {
+ return base.color
+ } else if (!base.enabled)
+ {
+ return UM.Theme.getColor("setting_category_disabled")
+ } else if (base.hovered && base.checkable && base.checked)
+ {
+ return UM.Theme.getColor("setting_category_active_hover")
+ } else if (base.pressed || (base.checkable && base.checked))
+ {
+ return UM.Theme.getColor("setting_category_active")
+ } else if (base.hovered)
+ {
+ return UM.Theme.getColor("setting_category_hover")
+ } else
+ {
+ return UM.Theme.getColor("setting_category")
}
}
Behavior on color { ColorAnimation { duration: 50; } }
@@ -40,17 +49,23 @@ Button
height: UM.Theme.getSize("default_lining").height
width: parent.width
anchors.bottom: parent.bottom
- color: {
- if (!base.enabled) {
- return UM.Theme.getColor("setting_category_disabled_border");
- } else if ((base.hovered || base.activeFocus) && base.checkable && base.checked) {
- return UM.Theme.getColor("setting_category_active_hover_border");
- } else if (base.pressed || (base.checkable && base.checked)) {
- return UM.Theme.getColor("setting_category_active_border");
- } else if (base.hovered || base.activeFocus) {
- return UM.Theme.getColor("setting_category_hover_border");
- } else {
- return UM.Theme.getColor("setting_category_border");
+ color:
+ {
+ if (!base.enabled)
+ {
+ return UM.Theme.getColor("setting_category_disabled_border")
+ } else if ((base.hovered || base.activeFocus) && base.checkable && base.checked)
+ {
+ return UM.Theme.getColor("setting_category_active_hover_border")
+ } else if (base.pressed || (base.checkable && base.checked))
+ {
+ return UM.Theme.getColor("setting_category_active_border")
+ } else if (base.hovered || base.activeFocus)
+ {
+ return UM.Theme.getColor("setting_category_hover_border")
+ } else
+ {
+ return UM.Theme.getColor("setting_category_border")
}
}
}
@@ -65,18 +80,19 @@ Button
property var focusItem: base
- contentItem: Item {
+ contentItem: Item
+ {
anchors.fill: parent
- anchors.left: parent.left
- Label {
+ Label
+ {
id: settingNameLabel
anchors
{
left: parent.left
leftMargin: 2 * UM.Theme.getSize("default_margin").width + UM.Theme.getSize("section_icon").width
- right: parent.right;
- verticalCenter: parent.verticalCenter;
+ right: parent.right
+ verticalCenter: parent.verticalCenter
}
text: definition.label
textFormat: Text.PlainText
@@ -84,21 +100,27 @@ Button
font: UM.Theme.getFont("setting_category")
color:
{
- if (!base.enabled) {
- return UM.Theme.getColor("setting_category_disabled_text");
- } else if ((base.hovered || base.activeFocus) && base.checkable && base.checked) {
- return UM.Theme.getColor("setting_category_active_hover_text");
- } else if (base.pressed || (base.checkable && base.checked)) {
- return UM.Theme.getColor("setting_category_active_text");
- } else if (base.hovered || base.activeFocus) {
- return UM.Theme.getColor("setting_category_hover_text");
- } else {
- return UM.Theme.getColor("setting_category_text");
+ if (!base.enabled)
+ {
+ return UM.Theme.getColor("setting_category_disabled_text")
+ } else if ((base.hovered || base.activeFocus) && base.checkable && base.checked)
+ {
+ return UM.Theme.getColor("setting_category_active_hover_text")
+ } else if (base.pressed || (base.checkable && base.checked))
+ {
+ return UM.Theme.getColor("setting_category_active_text")
+ } else if (base.hovered || base.activeFocus)
+ {
+ return UM.Theme.getColor("setting_category_hover_text")
+ } else
+ {
+ return UM.Theme.getColor("setting_category_text")
}
}
fontSizeMode: Text.HorizontalFit
minimumPointSize: 8
}
+
UM.RecolorImage
{
id: category_arrow
@@ -111,16 +133,21 @@ Button
sourceSize.height: width
color:
{
- if (!base.enabled) {
- return UM.Theme.getColor("setting_category_disabled_text");
- } else if ((base.hovered || base.activeFocus) && base.checkable && base.checked) {
- return UM.Theme.getColor("setting_category_active_hover_text");
- } else if (base.pressed || (base.checkable && base.checked)) {
- return UM.Theme.getColor("setting_category_active_text");
- } else if (base.hovered || base.activeFocus) {
- return UM.Theme.getColor("setting_category_hover_text");
- } else {
- return UM.Theme.getColor("setting_category_text");
+ if (!base.enabled)
+ {
+ return UM.Theme.getColor("setting_category_disabled_text")
+ } else if ((base.hovered || base.activeFocus) && base.checkable && base.checked)
+ {
+ return UM.Theme.getColor("setting_category_active_hover_text")
+ } else if (base.pressed || (base.checkable && base.checked))
+ {
+ return UM.Theme.getColor("setting_category_active_text")
+ } else if (base.hovered || base.activeFocus)
+ {
+ return UM.Theme.getColor("setting_category_hover_text")
+ } else
+ {
+ return UM.Theme.getColor("setting_category_text")
}
}
source: base.checked ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left")
@@ -135,21 +162,26 @@ Button
anchors.leftMargin: UM.Theme.getSize("default_margin").width
color:
{
- if (!base.enabled) {
- return UM.Theme.getColor("setting_category_disabled_text");
- } else if((base.hovered || base.activeFocus) && base.checkable && base.checked) {
- return UM.Theme.getColor("setting_category_active_hover_text");
- } else if(base.pressed || (base.checkable && base.checked)) {
- return UM.Theme.getColor("setting_category_active_text");
- } else if(base.hovered || base.activeFocus) {
- return UM.Theme.getColor("setting_category_hover_text");
- } else {
- return UM.Theme.getColor("setting_category_text");
+ if (!base.enabled)
+ {
+ return UM.Theme.getColor("setting_category_disabled_text")
+ } else if((base.hovered || base.activeFocus) && base.checkable && base.checked)
+ {
+ return UM.Theme.getColor("setting_category_active_hover_text")
+ } else if(base.pressed || (base.checkable && base.checked))
+ {
+ return UM.Theme.getColor("setting_category_active_text")
+ } else if(base.hovered || base.activeFocus)
+ {
+ return UM.Theme.getColor("setting_category_hover_text")
+ } else
+ {
+ return UM.Theme.getColor("setting_category_text")
}
}
source: UM.Theme.getIcon(definition.icon)
- width: UM.Theme.getSize("section_icon").width;
- height: UM.Theme.getSize("section_icon").height;
+ width: UM.Theme.getSize("section_icon").width
+ height: UM.Theme.getSize("section_icon").height
sourceSize.width: width + 15 * screenScaleFactor
sourceSize.height: width + 15 * screenScaleFactor
}
@@ -159,31 +191,26 @@ Button
onClicked:
{
- if (definition.expanded) {
- settingDefinitionsModel.collapse(definition.key);
+ if (definition.expanded)
+ {
+ settingDefinitionsModel.collapse(definition.key)
} else {
- settingDefinitionsModel.expandRecursive(definition.key);
+ settingDefinitionsModel.expandRecursive(definition.key)
}
//Set focus so that tab navigation continues from this point on.
//NB: This must be set AFTER collapsing/expanding the category so that the scroll position is correct.
- forceActiveFocus();
+ forceActiveFocus()
}
onActiveFocusChanged:
{
if(activeFocus)
{
- base.focusReceived();
+ base.focusReceived()
}
}
- Keys.onTabPressed:
- {
- base.setActiveFocusToNextSetting(true)
- }
- Keys.onBacktabPressed:
- {
- base.setActiveFocusToNextSetting(false)
- }
+ Keys.onTabPressed: base.setActiveFocusToNextSetting(true)
+ Keys.onBacktabPressed: base.setActiveFocusToNextSetting(false)
UM.SimpleButton
{
@@ -193,9 +220,10 @@ Button
height: Math.round(base.height * 0.6)
width: Math.round(base.height * 0.6)
- anchors {
+ anchors
+ {
right: inheritButton.visible ? inheritButton.left : parent.right
- // use 1.9 as the factor because there is a 0.1 difference between the settings and inheritance warning icons
+ // Use 1.9 as the factor because there is a 0.1 difference between the settings and inheritance warning icons
rightMargin: inheritButton.visible ? Math.round(UM.Theme.getSize("default_margin").width / 2) : category_arrow.width + Math.round(UM.Theme.getSize("default_margin").width * 1.9)
verticalCenter: parent.verticalCenter
}
@@ -204,9 +232,7 @@ Button
hoverColor: UM.Theme.getColor("setting_control_button_hover")
iconSource: UM.Theme.getIcon("settings")
- onClicked: {
- Cura.Actions.configureSettingVisibility.trigger(definition)
- }
+ onClicked: Cura.Actions.configureSettingVisibility.trigger(definition)
}
UM.SimpleButton
@@ -239,24 +265,18 @@ Button
onClicked:
{
- settingDefinitionsModel.expandRecursive(definition.key);
- base.checked = true;
- base.showAllHiddenInheritedSettings(definition.key);
+ settingDefinitionsModel.expandRecursive(definition.key)
+ base.checked = true
+ base.showAllHiddenInheritedSettings(definition.key)
}
color: UM.Theme.getColor("setting_control_button")
hoverColor: UM.Theme.getColor("setting_control_button_hover")
iconSource: UM.Theme.getIcon("notice")
- onEntered:
- {
- base.showTooltip(catalog.i18nc("@label","Some hidden settings use values different from their normal calculated value.\n\nClick to make these settings visible."))
- }
+ onEntered: base.showTooltip(catalog.i18nc("@label","Some hidden settings use values different from their normal calculated value.\n\nClick to make these settings visible."))
- onExited:
- {
- base.hideTooltip();
- }
+ onExited: base.hideTooltip()
UM.I18nCatalog { id: catalog; name: "cura" }
}
diff --git a/resources/qml/Toolbar.qml b/resources/qml/Toolbar.qml
index 0240aaab26..07522dd535 100644
--- a/resources/qml/Toolbar.qml
+++ b/resources/qml/Toolbar.qml
@@ -2,9 +2,7 @@
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.2
-import QtQuick.Controls 1.1
-import QtQuick.Controls.Styles 1.1
-import QtQuick.Layouts 1.1
+import QtQuick.Controls 2.3
import UM 1.2 as UM
import Cura 1.0 as Cura
@@ -55,17 +53,24 @@ Item
model: UM.ToolModel { id: toolsModel }
width: childrenRect.width
height: childrenRect.height
- Button
+
+ delegate: ToolbarButton
{
text: model.name + (model.shortcut ? (" (" + model.shortcut + ")") : "")
- iconSource: (UM.Theme.getIcon(model.icon) != "") ? UM.Theme.getIcon(model.icon) : "file:///" + model.location + "/" + model.icon
checkable: true
checked: model.active
enabled: model.enabled && UM.Selection.hasSelection && UM.Controller.toolsEnabled
- style: UM.Theme.styles.toolbar_button
- property bool isFirstElement: toolsModel.getItem(0).id == model.id
- property bool isLastElement: toolsModel.getItem(toolsModel.rowCount() - 1).id == model.id
+ isTopElement: toolsModel.getItem(0).id == model.id
+ isBottomElement: toolsModel.getItem(toolsModel.rowCount() - 1).id == model.id
+
+ toolItem: UM.RecolorImage
+ {
+ source: UM.Theme.getIcon(model.icon) != "" ? UM.Theme.getIcon(model.icon) : "file:///" + model.location + "/" + model.icon
+ color: UM.Theme.getColor("toolbar_button_text")
+
+ sourceSize: UM.Theme.getSize("button_icon")
+ }
onCheckedChanged:
{
@@ -128,11 +133,12 @@ Item
height: childrenRect.height
property var _model: Cura.ExtrudersModel { id: extrudersModel }
model: _model.items.length > 1 ? _model : 0
- ExtruderButton
+
+ delegate: ExtruderButton
{
extruder: model
- height: UM.Theme.getSize("button").width
- width: UM.Theme.getSize("button").width
+ isTopElement: extrudersModel.getItem(0).id == model.id
+ isBottomElement: extrudersModel.getItem(extrudersModel.rowCount() - 1).id == model.id
}
}
}
diff --git a/resources/qml/ToolbarButton.qml b/resources/qml/ToolbarButton.qml
new file mode 100644
index 0000000000..adff73fb7c
--- /dev/null
+++ b/resources/qml/ToolbarButton.qml
@@ -0,0 +1,99 @@
+// 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
+
+Button
+{
+ id: base
+
+ property alias toolItem: contentItemLoader.sourceComponent
+
+ // These two properties indicate whether the toolbar button is at the top of the toolbar column or at the bottom.
+ // If it is somewhere in the middle, then both has to be false. If there is only one element in the column, then
+ // both properties have to be set to true. This is used to create a rounded corner.
+ property bool isTopElement: false
+ property bool isBottomElement: false
+
+ hoverEnabled: true
+
+ background: Rectangle
+ {
+ implicitWidth: UM.Theme.getSize("button").width
+ implicitHeight: UM.Theme.getSize("button").height
+ color:
+ {
+ if (base.checked && base.hovered)
+ {
+ return UM.Theme.getColor("toolbar_button_active_hover")
+ }
+ else if (base.checked)
+ {
+ return UM.Theme.getColor("toolbar_button_active")
+ }
+ else if(base.hovered)
+ {
+ return UM.Theme.getColor("toolbar_button_hover")
+ }
+ return UM.Theme.getColor("toolbar_background")
+ }
+ radius: UM.Theme.getSize("default_radius").width
+
+ Rectangle
+ {
+ id: topSquare
+ anchors
+ {
+ left: parent.left
+ right: parent.right
+ top: parent.top
+ }
+ height: parent.radius
+ color: parent.color
+ visible: !base.isTopElement
+ }
+
+ Rectangle
+ {
+ id: bottomSquare
+ anchors
+ {
+ left: parent.left
+ right: parent.right
+ bottom: parent.bottom
+ }
+ height: parent.radius
+ color: parent.color
+ visible: !base.isBottomElement
+ }
+
+ Rectangle
+ {
+ id: leftSquare
+ anchors
+ {
+ left: parent.left
+ top: parent.top
+ bottom: parent.bottom
+ }
+ width: parent.radius
+ color: parent.color
+ }
+ }
+
+ contentItem: Item
+ {
+ opacity: parent.enabled ? 1.0 : 0.2
+ Loader
+ {
+ id: contentItemLoader
+ anchors.centerIn: parent
+ width: UM.Theme.getSize("button_icon").width
+ height: UM.Theme.getSize("button_icon").height
+ }
+ }
+}
diff --git a/resources/qml/ViewOrientationButton.qml b/resources/qml/ViewOrientationButton.qml
new file mode 100644
index 0000000000..682fd71b4e
--- /dev/null
+++ b/resources/qml/ViewOrientationButton.qml
@@ -0,0 +1,16 @@
+// Copyright (c) 2018 Ultimaker B.V.
+// Cura is released under the terms of the LGPLv3 or higher.
+
+import QtQuick 2.2
+
+import UM 1.4 as UM
+
+UM.SimpleButton
+{
+ width: UM.Theme.getSize("small_button").width
+ height: UM.Theme.getSize("small_button").height
+ hoverBackgroundColor: UM.Theme.getColor("small_button_hover")
+ hoverColor: UM.Theme.getColor("small_button_text_hover")
+ color: UM.Theme.getColor("small_button_text")
+ iconMargin: 0.5 * UM.Theme.getSize("wide_lining").width
+}
\ No newline at end of file
diff --git a/resources/qml/ViewOrientationControls.qml b/resources/qml/ViewOrientationControls.qml
index acf75b1b48..51ed6e3dcb 100644
--- a/resources/qml/ViewOrientationControls.qml
+++ b/resources/qml/ViewOrientationControls.qml
@@ -7,7 +7,7 @@ import QtQuick.Controls.Styles 1.1
import UM 1.4 as UM
-// View orientation Item
+// A row of buttons that control the view direction
Row
{
id: viewOrientationControl
@@ -16,43 +16,33 @@ Row
height: childrenRect.height
width: childrenRect.width
- // #1 3d view
- Button
+ ViewOrientationButton
{
iconSource: UM.Theme.getIcon("view_3d")
- style: UM.Theme.styles.small_tool_button
- onClicked:UM.Controller.rotateView("3d", 0)
+ onClicked: UM.Controller.rotateView("3d", 0)
}
- // #2 Front view
- Button
+ ViewOrientationButton
{
iconSource: UM.Theme.getIcon("view_front")
- style: UM.Theme.styles.small_tool_button
onClicked: UM.Controller.rotateView("home", 0)
}
- // #3 Top view
- Button
+ ViewOrientationButton
{
iconSource: UM.Theme.getIcon("view_top")
- style: UM.Theme.styles.small_tool_button
onClicked: UM.Controller.rotateView("y", 90)
}
- // #4 Left view
- Button
+ ViewOrientationButton
{
iconSource: UM.Theme.getIcon("view_left")
- style: UM.Theme.styles.small_tool_button
onClicked: UM.Controller.rotateView("x", 90)
}
- // #5 Right view
- Button
+ ViewOrientationButton
{
iconSource: UM.Theme.getIcon("view_right")
- style: UM.Theme.styles.small_tool_button
onClicked: UM.Controller.rotateView("x", -90)
}
}
diff --git a/resources/qml/ViewsSelector.qml b/resources/qml/ViewsSelector.qml
new file mode 100644
index 0000000000..e9fdd57177
--- /dev/null
+++ b/resources/qml/ViewsSelector.qml
@@ -0,0 +1,129 @@
+// 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
+
+Cura.ExpandableComponent
+{
+ id: viewSelector
+
+ popupPadding: UM.Theme.getSize("default_lining").width
+ popupAlignment: Cura.ExpandableComponent.PopupAlignment.AlignLeft
+ iconSource: expanded ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left")
+
+ property var viewModel: UM.ViewModel { }
+
+ property var activeView:
+ {
+ for (var i = 0; i < viewModel.rowCount(); i++)
+ {
+ if (viewModel.items[i].active)
+ {
+ return viewModel.items[i]
+ }
+ }
+ return null
+ }
+
+ Component.onCompleted:
+ {
+ // Nothing was active, so just return the first one (the list is sorted by priority, so the most
+ // important one should be returned)
+ if (activeView == null)
+ {
+ UM.Controller.setActiveView(viewModel.getItem(0).id)
+ }
+ }
+
+ headerItem: Item
+ {
+ Label
+ {
+ id: title
+ text: catalog.i18nc("@button", "View types")
+ verticalAlignment: Text.AlignVCenter
+ height: parent.height
+ elide: Text.ElideRight
+ font: UM.Theme.getFont("default")
+ color: UM.Theme.getColor("text_medium")
+ renderType: Text.NativeRendering
+ }
+
+ Label
+ {
+ text: viewSelector.activeView ? viewSelector.activeView.name : ""
+ verticalAlignment: Text.AlignVCenter
+ anchors
+ {
+ left: title.right
+ leftMargin: UM.Theme.getSize("default_margin").width
+ }
+ height: parent.height
+ elide: Text.ElideRight
+ font: UM.Theme.getFont("default")
+ color: UM.Theme.getColor("text")
+ renderType: Text.NativeRendering
+ }
+ }
+
+ popupItem: Column
+ {
+ id: viewSelectorPopup
+ width: viewSelector.width - 2 * viewSelector.popupPadding
+
+ // For some reason the height/width of the column gets set to 0 if this is not set...
+ Component.onCompleted:
+ {
+ height = implicitHeight
+ width = viewSelector.width - 2 * viewSelector.popupPadding
+ }
+
+ Repeater
+ {
+ id: viewsList
+ model: viewSelector.viewModel
+
+ delegate: Button
+ {
+ id: viewsSelectorButton
+ text: model.name
+ width: parent.width
+ height: UM.Theme.getSize("action_button").height
+ leftPadding: UM.Theme.getSize("default_margin").width
+ rightPadding: UM.Theme.getSize("default_margin").width
+ checkable: true
+ checked: viewSelector.activeView != null ? viewSelector.activeView.id == id : false
+
+ contentItem: Label
+ {
+ id: buttonText
+ text: viewsSelectorButton.text
+ color: UM.Theme.getColor("text")
+ font: UM.Theme.getFont("action_button")
+ renderType: Text.NativeRendering
+ verticalAlignment: Text.AlignVCenter
+ elide: Text.ElideRight
+ }
+
+ background: Rectangle
+ {
+ id: backgroundRect
+ color: viewsSelectorButton.hovered ? UM.Theme.getColor("action_button_hovered") : "transparent"
+ radius: UM.Theme.getSize("action_button_radius").width
+ border.width: UM.Theme.getSize("default_lining").width
+ border.color: viewsSelectorButton.checked ? UM.Theme.getColor("primary") : "transparent"
+ }
+
+ onClicked:
+ {
+ viewSelector.togglePopup()
+ UM.Controller.setActiveView(id)
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/resources/qml/qmldir b/resources/qml/qmldir
index d5e4106d33..2475f398f8 100644
--- a/resources/qml/qmldir
+++ b/resources/qml/qmldir
@@ -9,4 +9,8 @@ MaterialMenu 1.0 MaterialMenu.qml
NozzleMenu 1.0 NozzleMenu.qml
ActionPanelWidget 1.0 ActionPanelWidget.qml
IconLabel 1.0 IconLabel.qml
-OutputDevicesActionButton 1.0 OutputDevicesActionButton.qml
\ No newline at end of file
+OutputDevicesActionButton 1.0 OutputDevicesActionButton.qml
+ExpandableComponent 1.0 ExpandableComponent.qml
+PrinterTypeLabel 1.0 PrinterTypeLabel.qml
+ViewsSelector 1.0 ViewsSelector.qml
+ToolbarButton 1.0 ToolbarButton.qml
\ No newline at end of file
diff --git a/resources/quality/anycubic_i3_mega/anycubic_i3_mega_draft.inst.cfg b/resources/quality/anycubic_i3_mega/anycubic_i3_mega_draft.inst.cfg
index bb47f68574..e94b9f01d1 100644
--- a/resources/quality/anycubic_i3_mega/anycubic_i3_mega_draft.inst.cfg
+++ b/resources/quality/anycubic_i3_mega/anycubic_i3_mega_draft.inst.cfg
@@ -8,6 +8,7 @@ setting_version = 5
type = quality
quality_type = draft
weight = 0
+global_quality = True
[values]
acceleration_enabled = True
diff --git a/resources/quality/anycubic_i3_mega/anycubic_i3_mega_high.inst.cfg b/resources/quality/anycubic_i3_mega/anycubic_i3_mega_high.inst.cfg
index a3ae98deba..c8c4bf9a81 100644
--- a/resources/quality/anycubic_i3_mega/anycubic_i3_mega_high.inst.cfg
+++ b/resources/quality/anycubic_i3_mega/anycubic_i3_mega_high.inst.cfg
@@ -8,6 +8,7 @@ setting_version = 5
type = quality
quality_type = high
weight = 2
+global_quality = True
[values]
acceleration_enabled = True
diff --git a/resources/quality/anycubic_i3_mega/anycubic_i3_mega_normal.inst.cfg b/resources/quality/anycubic_i3_mega/anycubic_i3_mega_normal.inst.cfg
index 13846b9702..399c3ebc55 100644
--- a/resources/quality/anycubic_i3_mega/anycubic_i3_mega_normal.inst.cfg
+++ b/resources/quality/anycubic_i3_mega/anycubic_i3_mega_normal.inst.cfg
@@ -8,6 +8,7 @@ setting_version = 5
type = quality
quality_type = normal
weight = 1
+global_quality = True
[values]
acceleration_enabled = True
diff --git a/resources/themes/cura-dark/theme.json b/resources/themes/cura-dark/theme.json
index 62b1dbfa0f..34b944b25b 100644
--- a/resources/themes/cura-dark/theme.json
+++ b/resources/themes/cura-dark/theme.json
@@ -133,7 +133,6 @@
"slider_groove_border": [127, 127, 127, 255],
"slider_groove_fill": [245, 245, 245, 255],
"slider_handle": [255, 255, 255, 255],
- "slider_handle_hover": [77, 182, 226, 255],
"slider_handle_active": [68, 192, 255, 255],
"slider_text_background": [255, 255, 255, 255],
@@ -209,9 +208,6 @@
"quality_slider_unavailable": [179, 179, 179, 255],
"quality_slider_available": [255, 255, 255, 255],
- "quality_slider_handle": [255, 255, 255, 255],
- "quality_slider_handle_hover": [127, 127, 127, 255],
- "quality_slider_text": [255, 255, 255, 255],
"toolbox_header_button_text_active": [255, 255, 255, 255],
"toolbox_header_button_text_inactive": [128, 128, 128, 255],
diff --git a/resources/themes/cura-light/icons/connected.svg b/resources/themes/cura-light/icons/connected.svg
deleted file mode 100644
index 18423bb6c4..0000000000
--- a/resources/themes/cura-light/icons/connected.svg
+++ /dev/null
@@ -1,5 +0,0 @@
-
\ No newline at end of file
diff --git a/resources/themes/cura-light/icons/disconnected.svg b/resources/themes/cura-light/icons/disconnected.svg
deleted file mode 100644
index 019dff117e..0000000000
--- a/resources/themes/cura-light/icons/disconnected.svg
+++ /dev/null
@@ -1,6 +0,0 @@
-
\ No newline at end of file
diff --git a/resources/themes/cura-light/icons/tab_status_connected.svg b/resources/themes/cura-light/icons/printer_connected.svg
similarity index 100%
rename from resources/themes/cura-light/icons/tab_status_connected.svg
rename to resources/themes/cura-light/icons/printer_connected.svg
diff --git a/resources/themes/cura-light/icons/printer_group.svg b/resources/themes/cura-light/icons/printer_group.svg
index 614bea90b8..5e439faca4 100644
--- a/resources/themes/cura-light/icons/printer_group.svg
+++ b/resources/themes/cura-light/icons/printer_group.svg
@@ -1,12 +1,20 @@
-