Fix merge conflicts from ui_rework_40

CURA-5943
This commit is contained in:
Lipu Fei 2018-11-29 15:27:06 +01:00
commit 6db52dde46
130 changed files with 2998 additions and 1740 deletions

View file

@ -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
-------------

View file

@ -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;

View file

@ -19,4 +19,12 @@
<glob-deleteall/>
<glob pattern="*.obj"/>
</mime-type>
<mime-type type="text/x-gcode">
<sub-class-of type="text/plain"/>
<comment>Gcode file</comment>
<icon name="unknown"/>
<glob-deleteall/>
<glob pattern="*.gcode"/>
<glob pattern="*.g"/>
</mime-type>
</mime-info>

View file

@ -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()

View file

@ -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:

View file

@ -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)

View file

@ -21,6 +21,7 @@ class QualityProfilesDropDownMenuModel(ListModel):
AvailableRole = Qt.UserRole + 5
QualityGroupRole = Qt.UserRole + 6
QualityChangesGroupRole = Qt.UserRole + 7
IsExperimentalRole = Qt.UserRole + 8
def __init__(self, parent = None):
super().__init__(parent)
@ -32,6 +33,7 @@ class QualityProfilesDropDownMenuModel(ListModel):
self.addRoleName(self.AvailableRole, "available") #Whether the quality profile is available in our current nozzle + material.
self.addRoleName(self.QualityGroupRole, "quality_group")
self.addRoleName(self.QualityChangesGroupRole, "quality_changes_group")
self.addRoleName(self.IsExperimentalRole, "is_experimental")
self._application = Application.getInstance()
self._machine_manager = self._application.getMachineManager()
@ -74,7 +76,8 @@ class QualityProfilesDropDownMenuModel(ListModel):
"layer_height": layer_height,
"layer_height_unit": self._layer_height_unit,
"available": quality_group.is_available,
"quality_group": quality_group}
"quality_group": quality_group,
"is_experimental": quality_group.is_experimental}
item_list.append(item)

View file

@ -4,6 +4,9 @@
from typing import Dict, Optional, List, Set
from PyQt5.QtCore import QObject, pyqtSlot
from UM.Util import parseBool
from cura.Machines.ContainerNode import ContainerNode
@ -29,6 +32,7 @@ class QualityGroup(QObject):
self.nodes_for_extruders = {} # type: Dict[int, ContainerNode]
self.quality_type = quality_type
self.is_available = False
self.is_experimental = False
@pyqtSlot(result = str)
def getName(self) -> str:
@ -51,3 +55,17 @@ class QualityGroup(QObject):
for extruder_node in self.nodes_for_extruders.values():
result.append(extruder_node)
return result
def setGlobalNode(self, node: "ContainerNode") -> None:
self.node_for_global = node
# Update is_experimental flag
is_experimental = parseBool(node.getMetaDataEntry("is_experimental", False))
self.is_experimental |= is_experimental
def setExtruderNode(self, position: int, node: "ContainerNode") -> None:
self.nodes_for_extruders[position] = node
# Update is_experimental flag
is_experimental = parseBool(node.getMetaDataEntry("is_experimental", False))
self.is_experimental |= is_experimental

View file

@ -235,7 +235,7 @@ class QualityManager(QObject):
for quality_type, quality_node in node.quality_type_map.items():
quality_group = QualityGroup(quality_node.getMetaDataEntry("name", ""), quality_type)
quality_group.node_for_global = quality_node
quality_group.setGlobalNode(quality_node)
quality_group_dict[quality_type] = quality_group
break
@ -337,7 +337,7 @@ class QualityManager(QObject):
quality_group = quality_group_dict[quality_type]
if position not in quality_group.nodes_for_extruders:
quality_group.nodes_for_extruders[position] = quality_node
quality_group.setExtruderNode(position, quality_node)
# If the machine has its own specific qualities, for extruders, it should skip the global qualities
# and use the material/variant specific qualities.
@ -367,7 +367,7 @@ class QualityManager(QObject):
if node and node.quality_type_map:
for quality_type, quality_node in node.quality_type_map.items():
quality_group = QualityGroup(quality_node.getMetaDataEntry("name", ""), quality_type)
quality_group.node_for_global = quality_node
quality_group.setGlobalNode(quality_node)
quality_group_dict[quality_type] = quality_group
break

View file

@ -107,7 +107,7 @@ class VariantManager:
break
return variant_node
return self._machine_to_variant_dict_map[machine_definition_id].get(variant_type, {}).get(variant_name)
return self._machine_to_variant_dict_map.get(machine_definition_id, {}).get(variant_type, {}).get(variant_name)
def getVariantNodes(self, machine: "GlobalStack", variant_type: "VariantType") -> Dict[str, ContainerNode]:
machine_definition_id = machine.definition.getId()

View file

@ -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)

View file

@ -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()

View file

@ -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)
self._firmware_updater.updateFirmware(firmware_file)

View file

@ -1,7 +1,10 @@
# Copyright (c) 2015 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from typing import Optional
from UM.Application import Application
from UM.Math.Polygon import Polygon
from UM.Qt.QtApplication import QtApplication
from UM.Scene.SceneNode import SceneNode
from UM.Resources import Resources
from UM.Math.Color import Color
@ -16,7 +19,7 @@ class ConvexHullNode(SceneNode):
# location an object uses on the buildplate. This area (or area's in case of one at a time printing) is
# then displayed as a transparent shadow. If the adhesion type is set to raft, the area is extruded
# to represent the raft as well.
def __init__(self, node, hull, thickness, parent = None):
def __init__(self, node: SceneNode, hull: Optional[Polygon], thickness: float, parent: Optional[SceneNode] = None) -> None:
super().__init__(parent)
self.setCalculateBoundingBox(False)
@ -25,7 +28,11 @@ class ConvexHullNode(SceneNode):
# Color of the drawn convex hull
if not Application.getInstance().getIsHeadLess():
self._color = Color(*Application.getInstance().getTheme().getColor("convex_hull").getRgb())
theme = QtApplication.getInstance().getTheme()
if theme:
self._color = Color(*theme.getColor("convex_hull").getRgb())
else:
self._color = Color(0, 0, 0)
else:
self._color = Color(0, 0, 0)
@ -75,7 +82,7 @@ class ConvexHullNode(SceneNode):
return True
def _onNodeDecoratorsChanged(self, node):
def _onNodeDecoratorsChanged(self, node: SceneNode) -> None:
convex_hull_head = self._node.callDecoration("getConvexHullHead")
if convex_hull_head:
convex_hull_head_builder = MeshBuilder()

View file

@ -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:
@ -616,6 +618,14 @@ class MachineManager(QObject):
is_supported = self._current_quality_group.is_available
return is_supported
@pyqtProperty(bool, notify = activeQualityGroupChanged)
def isActiveQualityExperimental(self) -> bool:
is_experimental = False
if self._global_container_stack:
if self._current_quality_group:
is_experimental = self._current_quality_group.is_experimental
return is_experimental
## Returns whether there is anything unsupported in the current set-up.
#
# The current set-up signifies the global stack and all extruder stacks,
@ -646,7 +656,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 +672,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 +741,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 +919,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 +939,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 +1007,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 +1086,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 +1144,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 +1214,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 +1424,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 +1542,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

View file

@ -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.

View file

@ -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))

View file

@ -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.

View file

@ -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
}
}
}

View file

@ -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

View file

@ -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

View file

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1,014 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 774 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

View file

@ -9,10 +9,10 @@ import Cura 1.0 as Cura
Rectangle {
property var iconSource: null;
color: clickArea.containsMouse ? UM.Theme.getColor("primary_hover") : UM.Theme.getColor("primary"); // "Cura Blue"
color: "#0a0850" // TODO: Theme!
height: width;
radius: Math.round(0.5 * width);
width: 36 * screenScaleFactor;
width: 24 * screenScaleFactor;
UM.RecolorImage {
id: icon;

View file

@ -10,14 +10,13 @@ import QtGraphicalEffects 1.0
Component
{
Rectangle
Item
{
id: monitorFrame
property var emphasisColor: UM.Theme.getColor("setting_control_border_highlight")
property var cornerRadius: UM.Theme.getSize("monitor_corner_radius").width
color: "transparent"
height: maximumHeight
onVisibleChanged:
{
@ -48,13 +47,45 @@ Component
}
}
ScrollView
{
id: printers
anchors
{
left: queue.left
right: queue.right
top: parent.top
topMargin: 48 * screenScaleFactor // TODO: Theme!
}
height: 264 * screenScaleFactor // TODO: Theme!
Row
{
spacing: 60 * screenScaleFactor // TODO: Theme!
Repeater
{
model: OutputDevice.printers
MonitorPrinterCard
{
printer: modelData
}
}
}
}
Item
{
id: queue
width: Math.min(834 * screenScaleFactor, maximumWidth)
anchors.fill: parent
anchors.top: parent.top
anchors.topMargin: 400 * screenScaleFactor // TODO: Insert carousel here
anchors {
bottom: parent.bottom
horizontalCenter: parent.horizontalCenter
top: printers.bottom
topMargin: 48 * screenScaleFactor // TODO: Theme!
}
Label
{
@ -104,7 +135,6 @@ Component
text: catalog.i18nc("@label link to connect manager", "Manage queue in Cura Connect")
}
}
MouseArea
{
@ -187,7 +217,7 @@ Component
}
style: UM.Theme.styles.scrollview
visible: OutputDevice.receivedPrintJobs
width: Math.min(834 * screenScaleFactor, maximumWidth)
width: parent.width
ListView
{
@ -214,5 +244,4 @@ Component
visible: OutputDevice.activeCameraUrl != ""
}
}
}

View file

@ -0,0 +1,137 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.3
import QtQuick.Controls.Styles 1.3
import QtQuick.Controls 1.4
import UM 1.3 as UM
/**
* NOTE: For most labels, a fixed height with vertical alignment is used to make
* layouts more deterministic (like the fixed-size textboxes used in original
* mock-ups). This is also a stand-in for CSS's 'line-height' property. Denoted
* with '// FIXED-LINE-HEIGHT:'.
*/
Item
{
id: base
property var printJob: null
property var progress:
{
if (!printJob)
{
return 0
}
var result = printJob.timeElapsed / printJob.timeTotal
if (result > 1.0)
{
result = 1.0
}
return result
}
property var remainingTime:
{
if (!printJob) {
return 0
}
/* Sometimes total minus elapsed is less than 0. Use Math.max() to prevent remaining
time from ever being less than 0. Negative durations cause strange behavior such
as displaying "-1h -1m". */
return Math.max(printer.activePrintJob.timeTotal - printer.activePrintJob.timeElapsed, 0)
}
property var progressText:
{
if (!printJob)
{
return "";
}
switch (printJob.state)
{
case "wait_cleanup":
if (printJob.timeTotal > printJob.timeElapsed)
{
return catalog.i18nc("@label:status", "Aborted")
}
return catalog.i18nc("@label:status", "Finished")
case "pre_print":
case "sent_to_printer":
return catalog.i18nc("@label:status", "Preparing")
case "aborted":
return catalog.i18nc("@label:status", "Aborted")
case "wait_user_action":
return catalog.i18nc("@label:status", "Aborted")
case "pausing":
return catalog.i18nc("@label:status", "Pausing")
case "paused":
return OutputDevice.formatDuration( remainingTime )
case "resuming":
return catalog.i18nc("@label:status", "Resuming")
case "queued":
return catalog.i18nc("@label:status", "Action required")
default:
return OutputDevice.formatDuration( remainingTime )
}
}
width: childrenRect.width
height: 18 * screenScaleFactor // TODO: Theme!
ProgressBar
{
id: progressBar
anchors
{
verticalCenter: parent.verticalCenter
}
value: progress;
style: ProgressBarStyle
{
background: Rectangle
{
color: "#e4e4f2" // TODO: Theme!
implicitHeight: visible ? 8 * screenScaleFactor : 0 // TODO: Theme!
implicitWidth: 180 * screenScaleFactor // TODO: Theme!
radius: 4 * screenScaleFactor // TODO: Theme!
}
progress: Rectangle
{
id: progressItem;
color:
{
if (printJob)
{
var state = printJob.state
var inactiveStates = [
"pausing",
"paused",
"resuming",
"wait_cleanup"
]
if (inactiveStates.indexOf(state) > -1 && remainingTime > 0)
{
return UM.Theme.getColor("monitor_progress_fill_inactive")
}
}
return "#0a0850" // TODO: Theme!
}
radius: 4 * screenScaleFactor // TODO: Theme!
}
}
}
Label
{
id: progressLabel
anchors
{
left: progressBar.right
leftMargin: 18 * screenScaleFactor // TODO: Theme!
}
text: progressText
color: "#374355" // TODO: Theme!
width: contentWidth
font: UM.Theme.getFont("medium") // 14pt, regular
// FIXED-LINE-HEIGHT:
height: 18 * screenScaleFactor // TODO: Theme!
verticalAlignment: Text.AlignVCenter
}
}

View file

@ -0,0 +1,242 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.3
import QtQuick.Controls 2.0
import UM 1.3 as UM
/**
* A Printer Card is has two main components: the printer portion and the print
* job portion, the latter being paired in the UI when a print job is paired
* a printer in-cluster.
*
* NOTE: For most labels, a fixed height with vertical alignment is used to make
* layouts more deterministic (like the fixed-size textboxes used in original
* mock-ups). This is also a stand-in for CSS's 'line-height' property. Denoted
* with '// FIXED-LINE-HEIGHT:'.
*/
Item
{
id: base
// The printer which all printer data is derived from
property var printer: null
property var borderSize: 1 * screenScaleFactor // TODO: Theme, and remove from here
width: 834 * screenScaleFactor // TODO: Theme!
height: 216 * screenScaleFactor // TODO: Theme!
// Printer portion
Rectangle
{
id: printerInfo
border
{
color: "#EAEAEC" // TODO: Theme!
width: borderSize // TODO: Remove once themed
}
color: "white" // TODO: Theme!
width: parent.width
height: 144 * screenScaleFactor // TODO: Theme!
Row
{
anchors
{
left: parent.left
leftMargin: 36 * screenScaleFactor // TODO: Theme!
verticalCenter: parent.verticalCenter
}
spacing: 18 * screenScaleFactor // TODO: Theme!
Image
{
id: printerImage
width: 108 * screenScaleFactor // TODO: Theme!
height: 108 * screenScaleFactor // TODO: Theme!
fillMode: Image.PreserveAspectFit
source: "../png/" + printer.type + ".png"
mipmap: true
}
Item
{
anchors
{
verticalCenter: parent.verticalCenter
}
width: 216 * screenScaleFactor // TODO: Theme!
height: printerNameLabel.height + printerFamilyPill.height + 6 * screenScaleFactor // TODO: Theme!
Label
{
id: printerNameLabel
text: printer && printer.name ? printer.name : ""
color: "#414054" // TODO: Theme!
elide: Text.ElideRight
font: UM.Theme.getFont("large") // 16pt, bold
width: parent.width
// FIXED-LINE-HEIGHT:
height: 18 * screenScaleFactor // TODO: Theme!
verticalAlignment: Text.AlignVCenter
}
MonitorPrinterPill
{
id: printerFamilyPill
anchors
{
top: printerNameLabel.bottom
topMargin: 6 * screenScaleFactor // TODO: Theme!
left: printerNameLabel.left
}
text: printer.type
}
}
MonitorPrinterConfiguration
{
id: printerConfiguration
anchors.verticalCenter: parent.verticalCenter
buildplate: "Glass"
configurations:
[
base.printer.printerConfiguration.extruderConfigurations[0],
base.printer.printerConfiguration.extruderConfigurations[1]
]
height: 72 * screenScaleFactor // TODO: Theme!
}
}
PrintJobContextMenu
{
id: contextButton
anchors
{
right: parent.right
rightMargin: 12 * screenScaleFactor // TODO: Theme!
top: parent.top
topMargin: 12 * screenScaleFactor // TODO: Theme!
}
printJob: printer.activePrintJob
width: 36 * screenScaleFactor // TODO: Theme!
height: 36 * screenScaleFactor // TODO: Theme!
}
CameraButton
{
id: cameraButton;
anchors
{
right: parent.right
rightMargin: 20 * screenScaleFactor // TODO: Theme!
bottom: parent.bottom
bottomMargin: 20 * screenScaleFactor // TODO: Theme!
}
iconSource: "../svg/icons/camera.svg"
}
}
// Print job portion
Rectangle
{
id: printJobInfo
anchors
{
top: printerInfo.bottom
topMargin: -borderSize * screenScaleFactor // TODO: Theme!
}
border
{
color: "#EAEAEC" // TODO: Theme!
width: borderSize // TODO: Remove once themed
}
color: "white" // TODO: Theme!
height: 84 * screenScaleFactor + borderSize // TODO: Remove once themed
width: parent.width
Row
{
anchors
{
fill: parent
topMargin: 12 * screenScaleFactor + borderSize // TODO: Theme!
bottomMargin: 12 * screenScaleFactor // TODO: Theme!
leftMargin: 36 * screenScaleFactor // TODO: Theme!
}
height: childrenRect.height
spacing: 18 * screenScaleFactor // TODO: Theme!
Item
{
anchors
{
verticalCenter: parent.verticalCenter
}
width: printerImage.width
height: 60 * screenScaleFactor // TODO: Theme!
MonitorPrintJobPreview
{
anchors.centerIn: parent
printJob: base.printer.activePrintJob
size: parent.height
}
}
Item
{
anchors
{
verticalCenter: parent.verticalCenter
}
width: 216 * screenScaleFactor // TODO: Theme!
height: printerNameLabel.height + printerFamilyPill.height + 6 * screenScaleFactor // TODO: Theme!
Label
{
id: printerJobNameLabel
text: base.printer.activePrintJob ? base.printer.activePrintJob.name : "Untitled" // TODO: I18N
color: "#414054" // TODO: Theme!
elide: Text.ElideRight
font: UM.Theme.getFont("large") // 16pt, bold
width: parent.width
// FIXED-LINE-HEIGHT:
height: 18 * screenScaleFactor // TODO: Theme!
verticalAlignment: Text.AlignVCenter
}
Label
{
id: printerJobOwnerLabel
anchors
{
top: printerJobNameLabel.bottom
topMargin: 6 * screenScaleFactor // TODO: Theme!
left: printerJobNameLabel.left
}
text: printer.activePrintJob ? printer.activePrintJob.owner : "Anonymous" // TODO: I18N
color: "#53657d" // TODO: Theme!
elide: Text.ElideRight
font: UM.Theme.getFont("very_small") // 12pt, regular
width: parent.width
// FIXED-LINE-HEIGHT:
height: 18 * screenScaleFactor // TODO: Theme!
verticalAlignment: Text.AlignVCenter
}
}
MonitorPrintJobProgressBar
{
anchors
{
verticalCenter: parent.verticalCenter
}
printJob: printer.activePrintJob
}
}
}
}

View file

@ -25,7 +25,7 @@ Item {
}
contentItem: Label {
color: UM.Theme.getColor("monitor_context_menu_dots");
font.pixelSize: 25 * screenScaleFactor;
font.pixelSize: 32 * screenScaleFactor;
horizontalAlignment: Text.AlignHCenter;
text: button.text;
verticalAlignment: Text.AlignVCenter;
@ -41,7 +41,7 @@ Item {
var states = ["queued", "sent_to_printer", "pre_print", "printing", "pausing", "paused", "resuming"];
return states.indexOf(printJob.state) !== -1;
}
width: 35 * screenScaleFactor; // TODO: Theme!
width: 36 * screenScaleFactor; // TODO: Theme!
}
Popup {

View file

@ -0,0 +1,5 @@
<svg width="12px" height="12px" viewBox="0 0 12 12" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<path d="M4.298828,0.998047 L7.7,0.998047 L8.7,3 L12,3 L12,10 L0,10 L0,3 L3.3,3 L4.298828,0.998047 Z M6,4 C4.625211,4 3.5,5.1252 3.5,6.5 C3.5,7.8748 4.625211,9 6,9 C7.37479,9 8.5,7.8748 8.5,6.5 C8.5,5.1252 7.37479,4 6,4 Z M6,5 C6.83435,5 7.5,5.6657 7.5,6.5 C7.5,7.3343 6.83435,8 6,8 C5.165651,8 4.5,7.3343 4.5,6.5 C4.5,5.6657 5.165651,5 6,5 Z" id="Combined-Shape" fill="#0A0850" fill-rule="nonzero"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 658 B

View file

@ -65,7 +65,6 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
self._received_print_jobs = False # type: bool
self._monitor_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../resources/qml/ClusterMonitorItem.qml")
self._control_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../resources/qml/ClusterControlItem.qml")
# See comments about this hack with the clusterPrintersChanged signal
self.printersChanged.connect(self.clusterPrintersChanged)

View file

@ -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")

View file

@ -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"))
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

View file

@ -1,4 +1,4 @@
# Copyright (c) 2017 Ultimaker B.V.
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
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)

View file

@ -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("<xml></xml>"))
@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\"", "<xml></xml>"),
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\"", "<xml></xml>"),
call.postFormWithParts(target = "materials/", parts = ["_xXx_"], on_finished = job.sendingFinished)],
device_mock.method_calls)

View file

@ -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.

View file

@ -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
}
}
}

View file

@ -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
}
}
}

View file

@ -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.10
},
"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
}
}
}

View file

@ -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
}
}
}

View file

@ -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
}
}
}

View file

@ -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
}
}
}

View file

@ -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"
}
}
}

View file

@ -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"
}
}
}

View file

@ -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"
}
}
}

View file

@ -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"
}
}
}

View file

@ -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"
}
}
}

View file

@ -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"
}
}
}

View file

@ -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"
}
}
}

View file

@ -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"
}
}
}

View file

@ -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 }
}
}

View file

@ -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"
}
}
}

View file

@ -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"
}
}
}

View file

@ -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 }
}
}

View file

@ -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 }
}
}

View file

@ -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 }
}
}

View file

@ -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 }
}
}

Binary file not shown.

View file

@ -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

View file

@ -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

View file

@ -3,27 +3,34 @@
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 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.
@ -68,6 +75,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
@ -75,12 +95,4 @@ Button
delay: 500
visible: text != "" && button.hovered
}
MouseArea
{
id: mouseArea
anchors.fill: parent
onPressed: mouse.accepted = false
hoverEnabled: true
}
}

View file

@ -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
}
}
}

View file

@ -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

View file

@ -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
}
}

View file

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

View file

@ -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

View file

@ -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

View file

@ -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
@ -46,6 +60,12 @@ Item
// On what side should the header corners be shown? 1 is down, 2 is left, 3 is up and 4 is right.
property alias headerCornerSide: background.cornerSide
property alias headerShadowColor: shadow.color
property alias enableHeaderShadow: shadow.visible
property int shadowOffset: 2
function togglePopup()
{
if(popup.visible)
@ -123,8 +143,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
@ -137,23 +157,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
y: headerItemLoader.height + 2 * background.padding + base.shadowOffset
// 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
}
}
}

View file

@ -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)
}
}

View file

@ -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

View file

@ -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

View file

@ -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)
}
}
}
}
}
}
}

View file

@ -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

View file

@ -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
}

View file

@ -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)
}
}

View file

@ -17,18 +17,21 @@ Menu
MenuItem
{
text: (model.layer_height != "") ? model.name + " - " + model.layer_height + model.layer_height_unit : model.name
text:
{
var full_text = (model.layer_height != "") ? model.name + " - " + model.layer_height + model.layer_height_unit : model.name
full_text += model.is_experimental ? " - Experimental" : ""
return full_text
}
checkable: true
checked: Cura.MachineManager.activeQualityOrQualityChangesName == model.name
exclusiveGroup: group
onTriggered: {
Cura.MachineManager.setQualityGroup(model.quality_group)
}
onTriggered: Cura.MachineManager.setQualityGroup(model.quality_group)
visible: model.available
}
onObjectAdded: menu.insertItem(index, object);
onObjectRemoved: menu.removeItem(object);
onObjectAdded: menu.insertItem(index, object)
onObjectRemoved: menu.removeItem(object)
}
MenuSeparator

View file

@ -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")
}

View file

@ -54,12 +54,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"]
}
@ -68,12 +68,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"]
}

View file

@ -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()
}
}
}
}
}

View file

@ -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()
}

View file

@ -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
}
}
}

View file

@ -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")
}
}

View file

@ -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
}
}
}
}

View file

@ -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 }
}
}

View file

@ -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")
}

View file

@ -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" }
}

View file

@ -62,11 +62,18 @@ Item
activeFocusOnPress: true
menu: ProfileMenu { }
function generateActiveQualityText () {
var result = Cura.MachineManager.activeQualityOrQualityChangesName;
function generateActiveQualityText ()
{
var result = Cura.MachineManager.activeQualityOrQualityChangesName
if (Cura.MachineManager.isActiveQualityExperimental)
{
result += " (Experimental)"
}
if (Cura.MachineManager.isActiveQualitySupported) {
if (Cura.MachineManager.activeQualityLayerHeight > 0) {
if (Cura.MachineManager.isActiveQualitySupported)
{
if (Cura.MachineManager.activeQualityLayerHeight > 0)
{
result += " <font color=\"" + UM.Theme.getColor("text_detail") + "\">"
result += " - "
result += Cura.MachineManager.activeQualityLayerHeight + "mm"

View file

@ -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
}
}
}

View file

@ -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
}
}
}

View file

@ -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
}

View file

@ -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)
}
}

View file

@ -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)
}
}
}
}
}

View file

@ -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
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

View file

@ -8,6 +8,7 @@ setting_version = 5
type = quality
quality_type = draft
weight = 0
global_quality = True
[values]
acceleration_enabled = True

View file

@ -8,6 +8,7 @@ setting_version = 5
type = quality
quality_type = high
weight = 2
global_quality = True
[values]
acceleration_enabled = True

View file

@ -8,6 +8,7 @@ setting_version = 5
type = quality
quality_type = normal
weight = 1
global_quality = True
[values]
acceleration_enabled = True

View file

@ -10,6 +10,7 @@ quality_type = normal
weight = 0
material = generic_pc
variant = AA 0.25
is_experimental = True
[values]
acceleration_enabled = True

View file

@ -10,6 +10,7 @@ quality_type = normal
weight = 0
material = generic_pp
variant = AA 0.25
is_experimental = True
[values]
acceleration_enabled = True

View file

@ -10,6 +10,7 @@ quality_type = draft
weight = -2
material = generic_cpe_plus
variant = AA 0.8
is_experimental = True
[values]
brim_width = 14

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