diff --git a/cura/BuildVolume.py b/cura/BuildVolume.py index 1691361629..707a5df2a7 100644 --- a/cura/BuildVolume.py +++ b/cura/BuildVolume.py @@ -34,16 +34,18 @@ PRIME_CLEARANCE = 6.5 ## Build volume is a special kind of node that is responsible for rendering the printable area & disallowed areas. class BuildVolume(SceneNode): - VolumeOutlineColor = Color(12, 169, 227, 255) - XAxisColor = Color(255, 0, 0, 255) - YAxisColor = Color(0, 0, 255, 255) - ZAxisColor = Color(0, 255, 0, 255) - raftThicknessChanged = Signal() def __init__(self, parent = None): super().__init__(parent) + self._volume_outline_color = None + self._x_axis_color = None + self._y_axis_color = None + self._z_axis_color = None + self._disallowed_area_color = None + self._error_area_color = None + self._width = 0 self._height = 0 self._depth = 0 @@ -75,6 +77,9 @@ class BuildVolume(SceneNode): Application.getInstance().globalContainerStackChanged.connect(self._onStackChanged) self._onStackChanged() + self._engine_ready = False + Application.getInstance().engineCreatedSignal.connect(self._onEngineCreated) + self._has_errors = False Application.getInstance().getController().getScene().sceneChanged.connect(self._onSceneChanged) @@ -99,6 +104,7 @@ class BuildVolume(SceneNode): # but it does not update the disallowed areas after material change Application.getInstance().getMachineManager().activeStackChanged.connect(self._onStackChanged) + def _onSceneChanged(self, source): if self._global_container_stack: self._change_timer.start() @@ -158,6 +164,9 @@ class BuildVolume(SceneNode): if not self._shader: self._shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "default.shader")) self._grid_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "grid.shader")) + theme = Application.getInstance().getTheme() + self._grid_shader.setUniformValue("u_gridColor0", Color(*theme.getColor("buildplate").getRgb())) + self._grid_shader.setUniformValue("u_gridColor1", Color(*theme.getColor("buildplate_alt").getRgb())) renderer.queueNode(self, mode = RenderBatch.RenderMode.Lines) renderer.queueNode(self, mesh = self._origin_mesh) @@ -176,6 +185,18 @@ class BuildVolume(SceneNode): if not self._width or not self._height or not self._depth: return + if not Application.getInstance()._engine: + return + + if not self._volume_outline_color: + theme = Application.getInstance().getTheme() + self._volume_outline_color = Color(*theme.getColor("volume_outline").getRgb()) + self._x_axis_color = Color(*theme.getColor("x_axis").getRgb()) + self._y_axis_color = Color(*theme.getColor("y_axis").getRgb()) + self._z_axis_color = Color(*theme.getColor("z_axis").getRgb()) + self._disallowed_area_color = Color(*theme.getColor("disallowed_area").getRgb()) + self._error_area_color = Color(*theme.getColor("error_area").getRgb()) + min_w = -self._width / 2 max_w = self._width / 2 min_h = 0.0 @@ -188,20 +209,20 @@ class BuildVolume(SceneNode): if self._shape != "elliptic": # Outline 'cube' of the build volume mb = MeshBuilder() - mb.addLine(Vector(min_w, min_h, min_d), Vector(max_w, min_h, min_d), color = self.VolumeOutlineColor) - mb.addLine(Vector(min_w, min_h, min_d), Vector(min_w, max_h, min_d), color = self.VolumeOutlineColor) - mb.addLine(Vector(min_w, max_h, min_d), Vector(max_w, max_h, min_d), color = self.VolumeOutlineColor) - mb.addLine(Vector(max_w, min_h, min_d), Vector(max_w, max_h, min_d), color = self.VolumeOutlineColor) + mb.addLine(Vector(min_w, min_h, min_d), Vector(max_w, min_h, min_d), color = self._volume_outline_color) + mb.addLine(Vector(min_w, min_h, min_d), Vector(min_w, max_h, min_d), color = self._volume_outline_color) + mb.addLine(Vector(min_w, max_h, min_d), Vector(max_w, max_h, min_d), color = self._volume_outline_color) + mb.addLine(Vector(max_w, min_h, min_d), Vector(max_w, max_h, min_d), color = self._volume_outline_color) - mb.addLine(Vector(min_w, min_h, max_d), Vector(max_w, min_h, max_d), color = self.VolumeOutlineColor) - mb.addLine(Vector(min_w, min_h, max_d), Vector(min_w, max_h, max_d), color = self.VolumeOutlineColor) - mb.addLine(Vector(min_w, max_h, max_d), Vector(max_w, max_h, max_d), color = self.VolumeOutlineColor) - mb.addLine(Vector(max_w, min_h, max_d), Vector(max_w, max_h, max_d), color = self.VolumeOutlineColor) + mb.addLine(Vector(min_w, min_h, max_d), Vector(max_w, min_h, max_d), color = self._volume_outline_color) + mb.addLine(Vector(min_w, min_h, max_d), Vector(min_w, max_h, max_d), color = self._volume_outline_color) + mb.addLine(Vector(min_w, max_h, max_d), Vector(max_w, max_h, max_d), color = self._volume_outline_color) + mb.addLine(Vector(max_w, min_h, max_d), Vector(max_w, max_h, max_d), color = self._volume_outline_color) - mb.addLine(Vector(min_w, min_h, min_d), Vector(min_w, min_h, max_d), color = self.VolumeOutlineColor) - mb.addLine(Vector(max_w, min_h, min_d), Vector(max_w, min_h, max_d), color = self.VolumeOutlineColor) - mb.addLine(Vector(min_w, max_h, min_d), Vector(min_w, max_h, max_d), color = self.VolumeOutlineColor) - mb.addLine(Vector(max_w, max_h, min_d), Vector(max_w, max_h, max_d), color = self.VolumeOutlineColor) + mb.addLine(Vector(min_w, min_h, min_d), Vector(min_w, min_h, max_d), color = self._volume_outline_color) + mb.addLine(Vector(max_w, min_h, min_d), Vector(max_w, min_h, max_d), color = self._volume_outline_color) + mb.addLine(Vector(min_w, max_h, min_d), Vector(min_w, max_h, max_d), color = self._volume_outline_color) + mb.addLine(Vector(max_w, max_h, min_d), Vector(max_w, max_h, max_d), color = self._volume_outline_color) self.setMeshData(mb.build()) @@ -228,8 +249,8 @@ class BuildVolume(SceneNode): aspect = self._depth / self._width scale_matrix.compose(scale = Vector(1, 1, aspect)) mb = MeshBuilder() - mb.addArc(max_w, Vector.Unit_Y, center = (0, min_h - z_fight_distance, 0), color = self.VolumeOutlineColor) - mb.addArc(max_w, Vector.Unit_Y, center = (0, max_h, 0), color = self.VolumeOutlineColor) + mb.addArc(max_w, Vector.Unit_Y, center = (0, min_h - z_fight_distance, 0), color = self._volume_outline_color) + mb.addArc(max_w, Vector.Unit_Y, center = (0, max_h, 0), color = self._volume_outline_color) self.setMeshData(mb.build().getTransformed(scale_matrix)) # Build plate grid mesh @@ -260,21 +281,21 @@ class BuildVolume(SceneNode): height = self._origin_line_width, depth = self._origin_line_width, center = origin + Vector(self._origin_line_length / 2, 0, 0), - color = self.XAxisColor + color = self._x_axis_color ) mb.addCube( width = self._origin_line_width, height = self._origin_line_length, depth = self._origin_line_width, center = origin + Vector(0, self._origin_line_length / 2, 0), - color = self.YAxisColor + color = self._y_axis_color ) mb.addCube( width = self._origin_line_width, height = self._origin_line_width, depth = self._origin_line_length, center = origin - Vector(0, 0, self._origin_line_length / 2), - color = self.ZAxisColor + color = self._z_axis_color ) self._origin_mesh = mb.build() @@ -282,7 +303,7 @@ class BuildVolume(SceneNode): disallowed_area_size = 0 if self._disallowed_areas: mb = MeshBuilder() - color = Color(0.0, 0.0, 0.0, 0.15) + color = self._disallowed_area_color for polygon in self._disallowed_areas: points = polygon.getPoints() if len(points) == 0: @@ -311,7 +332,7 @@ class BuildVolume(SceneNode): if self._error_areas: mb = MeshBuilder() for error_area in self._error_areas: - color = Color(1.0, 0.0, 0.0, 0.5) + color = self._error_area_color points = error_area.getPoints() first = Vector(self._clamp(points[0][0], min_w, max_w), disallowed_area_height, self._clamp(points[0][1], min_d, max_d)) @@ -398,7 +419,12 @@ class BuildVolume(SceneNode): self._updateDisallowedAreas() self._updateRaftThickness() - self.rebuild() + if self._engine_ready: + self.rebuild() + + def _onEngineCreated(self): + self._engine_ready = True + self.rebuild() def _onSettingPropertyChanged(self, setting_key, property_name): if property_name != "value": diff --git a/cura/ConvexHullNode.py b/cura/ConvexHullNode.py index 8e5acf9518..7282b0ffb2 100644 --- a/cura/ConvexHullNode.py +++ b/cura/ConvexHullNode.py @@ -1,6 +1,7 @@ # Copyright (c) 2015 Ultimaker B.V. # Cura is released under the terms of the AGPLv3 or higher. +from UM.Application import Application from UM.Scene.SceneNode import SceneNode from UM.Resources import Resources from UM.Math.Color import Color @@ -23,7 +24,7 @@ class ConvexHullNode(SceneNode): self._original_parent = parent # Color of the drawn convex hull - self._color = Color(0.4, 0.4, 0.4, 1.0) + self._color = None # The y-coordinate of the convex hull mesh. Must not be 0, to prevent z-fighting. self._mesh_height = 0.1 @@ -72,7 +73,7 @@ class ConvexHullNode(SceneNode): return True def _onNodeDecoratorsChanged(self, node): - self._color = Color(35, 35, 35, 0.5) + self._color = Color(*Application.getInstance().getTheme().getColor("convex_hull").getRgb()) convex_hull_head = self._node.callDecoration("getConvexHullHead") if convex_hull_head: diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 2ef5e08ef5..6e6db6c314 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -223,6 +223,10 @@ class CuraApplication(QtApplication): Preferences.getInstance().addPreference("mesh/scale_tiny_meshes", True) Preferences.getInstance().addPreference("cura/dialog_on_project_save", True) Preferences.getInstance().addPreference("cura/asked_dialog_on_project_save", False) + + Preferences.getInstance().addPreference("cura/currency", "€") + Preferences.getInstance().addPreference("cura/material_settings", "{}") + for key in [ "dialog_load_path", # dialog_save_path is in LocalFileOutputDevicePlugin "dialog_profile_path", @@ -315,6 +319,11 @@ class CuraApplication(QtApplication): showPrintMonitor = pyqtSignal(bool, arguments = ["show"]) + def setViewLegendItems(self, items): + self.viewLegendItemsChanged.emit(items) + + viewLegendItemsChanged = pyqtSignal("QVariantList", arguments = ["items"]) + ## Cura has multiple locations where instance containers need to be saved, so we need to handle this differently. # # Note that the AutoSave plugin also calls this method. diff --git a/cura/LayerPolygon.py b/cura/LayerPolygon.py index 34bb38249a..90bc123548 100644 --- a/cura/LayerPolygon.py +++ b/cura/LayerPolygon.py @@ -1,4 +1,5 @@ from UM.Math.Color import Color +from UM.Application import Application import numpy @@ -40,6 +41,7 @@ class LayerPolygon: # Buffering the colors shouldn't be necessary as it is not # re-used and can save alot of memory usage. + self._color_map = LayerPolygon.getColorMap() self._colors = self._color_map[self._types] # When type is used as index returns true if type == LayerPolygon.InfillType or type == LayerPolygon.SkinType or type == LayerPolygon.SupportInfillType @@ -182,17 +184,25 @@ class LayerPolygon: return normals - # Should be generated in better way, not hardcoded. - _color_map = numpy.array([ - [1.0, 1.0, 1.0, 1.0], # NoneType - [1.0, 0.0, 0.0, 1.0], # Inset0Type - [0.0, 1.0, 0.0, 1.0], # InsetXType - [1.0, 1.0, 0.0, 1.0], # SkinType - [0.0, 1.0, 1.0, 1.0], # SupportType - [0.0, 1.0, 1.0, 1.0], # SkirtType - [1.0, 0.75, 0.0, 1.0], # InfillType - [0.0, 1.0, 1.0, 1.0], # SupportInfillType - [0.0, 0.0, 1.0, 1.0], # MoveCombingType - [0.5, 0.5, 1.0, 1.0], # MoveRetractionType - [0.25, 0.75, 1.0, 1.0] # SupportInterfaceType - ]) \ No newline at end of file + __color_map = None + + ## Gets the instance of the VersionUpgradeManager, or creates one. + @classmethod + def getColorMap(cls): + if cls.__color_map is None: + theme = Application.getInstance().getTheme() + cls.__color_map = numpy.array([ + theme.getColor("layerview_none").getRgbF(), # NoneType + theme.getColor("layerview_inset_0").getRgbF(), # Inset0Type + theme.getColor("layerview_inset_x").getRgbF(), # InsetXType + theme.getColor("layerview_skin").getRgbF(), # SkinType + theme.getColor("layerview_support").getRgbF(), # SupportType + theme.getColor("layerview_skirt").getRgbF(), # SkirtType + theme.getColor("layerview_infill").getRgbF(), # InfillType + theme.getColor("layerview_support_infill").getRgbF(), # SupportInfillType + theme.getColor("layerview_move_combing").getRgbF(), # MoveCombingType + theme.getColor("layerview_move_retraction").getRgbF(), # MoveRetractionType + theme.getColor("layerview_support_interface").getRgbF() # SupportInterfaceType + ]) + + return cls.__color_map diff --git a/cura/PrintInformation.py b/cura/PrintInformation.py index 7834ef77bd..769df189c5 100644 --- a/cura/PrintInformation.py +++ b/cura/PrintInformation.py @@ -7,12 +7,14 @@ from UM.FlameProfiler import pyqtSlot from UM.Application import Application from UM.Qt.Duration import Duration from UM.Preferences import Preferences +from UM.Settings import ContainerRegistry import cura.Settings.ExtruderManager import math import os.path import unicodedata +import json from UM.i18n import i18nCatalog catalog = i18nCatalog("cura") @@ -52,6 +54,7 @@ class PrintInformation(QObject): self._material_lengths = [] self._material_weights = [] + self._material_costs = [] self._pre_sliced = False @@ -65,6 +68,12 @@ class PrintInformation(QObject): Application.getInstance().globalContainerStackChanged.connect(self._setAbbreviatedMachineName) Application.getInstance().fileLoaded.connect(self.setJobName) + Preferences.getInstance().preferenceChanged.connect(self._onPreferencesChanged) + + self._active_material_container = None + Application.getInstance().getMachineManager().activeMaterialChanged.connect(self._onActiveMaterialChanged) + self._onActiveMaterialChanged() + currentPrintTimeChanged = pyqtSignal() preSlicedChanged = pyqtSignal() @@ -93,28 +102,82 @@ class PrintInformation(QObject): def materialWeights(self): return self._material_weights + materialCostsChanged = pyqtSignal() + + @pyqtProperty("QVariantList", notify = materialCostsChanged) + def materialCosts(self): + return self._material_costs + def _onPrintDurationMessage(self, total_time, material_amounts): self._current_print_time.setDuration(total_time) self.currentPrintTimeChanged.emit() + self._material_amounts = material_amounts + self._calculateInformation() + + def _calculateInformation(self): # Material amount is sent as an amount of mm^3, so calculate length from that r = Application.getInstance().getGlobalContainerStack().getProperty("material_diameter", "value") / 2 self._material_lengths = [] self._material_weights = [] + self._material_costs = [] + + material_preference_values = json.loads(Preferences.getInstance().getValue("cura/material_settings")) + extruder_stacks = list(cura.Settings.ExtruderManager.getInstance().getMachineExtruders(Application.getInstance().getGlobalContainerStack().getId())) - for index, amount in enumerate(material_amounts): + for index, amount in enumerate(self._material_amounts): ## Find the right extruder stack. As the list isn't sorted because it's a annoying generator, we do some # list comprehension filtering to solve this for us. + material = None if extruder_stacks: # Multi extrusion machine extruder_stack = [extruder for extruder in extruder_stacks if extruder.getMetaDataEntry("position") == str(index)][0] density = extruder_stack.getMetaDataEntry("properties", {}).get("density", 0) + material = extruder_stack.findContainer({"type": "material"}) else: # Machine with no extruder stacks density = Application.getInstance().getGlobalContainerStack().getMetaDataEntry("properties", {}).get("density", 0) + material = Application.getInstance().getGlobalContainerStack().findContainer({"type": "material"}) - self._material_weights.append(float(amount) * float(density) / 1000) + weight = float(amount) * float(density) / 1000 + cost = 0 + if material: + material_guid = material.getMetaDataEntry("GUID") + if material_guid in material_preference_values: + material_values = material_preference_values[material_guid] + + weight_per_spool = float(material_values["spool_weight"] if material_values and "spool_weight" in material_values else 0) + cost_per_spool = float(material_values["spool_cost"] if material_values and "spool_cost" in material_values else 0) + + if weight_per_spool != 0: + cost = cost_per_spool * weight / weight_per_spool + else: + cost = 0 + + self._material_weights.append(weight) self._material_lengths.append(round((amount / (math.pi * r ** 2)) / 1000, 2)) + self._material_costs.append(cost) + self.materialLengthsChanged.emit() self.materialWeightsChanged.emit() + self.materialCostsChanged.emit() + + def _onPreferencesChanged(self, preference): + if preference != "cura/material_settings": + return + + self._calculateInformation() + + def _onActiveMaterialChanged(self): + if self._active_material_container: + self._active_material_container.metaDataChanged.disconnect(self._onMaterialMetaDataChanged) + + active_material_id = Application.getInstance().getMachineManager().activeMaterialId + self._active_material_container = ContainerRegistry.getInstance().findInstanceContainers(id=active_material_id)[0] + + if self._active_material_container: + self._active_material_container.metaDataChanged.connect(self._onMaterialMetaDataChanged) + + def _onMaterialMetaDataChanged(self): + self._calculateInformation() @pyqtSlot(str) def setJobName(self, name): diff --git a/cura/Settings/ExtruderManager.py b/cura/Settings/ExtruderManager.py index 81579f74d0..e57ff3115e 100644 --- a/cura/Settings/ExtruderManager.py +++ b/cura/Settings/ExtruderManager.py @@ -96,8 +96,9 @@ class ExtruderManager(QObject): # \param index The index of the new active extruder. @pyqtSlot(int) def setActiveExtruderIndex(self, index): - self._active_extruder_index = index - self.activeExtruderChanged.emit() + if self._active_extruder_index != index: + self._active_extruder_index = index + self.activeExtruderChanged.emit() @pyqtProperty(int, notify = activeExtruderChanged) def activeExtruderIndex(self): diff --git a/plugins/3MFReader/WorkspaceDialog.py b/plugins/3MFReader/WorkspaceDialog.py index 8e16404b0a..1bae9575f2 100644 --- a/plugins/3MFReader/WorkspaceDialog.py +++ b/plugins/3MFReader/WorkspaceDialog.py @@ -70,8 +70,9 @@ class WorkspaceDialog(QObject): return self._variant_type def setVariantType(self, variant_type): - self._variant_type = variant_type - self.variantTypeChanged.emit() + if self._variant_type != variant_type: + self._variant_type = variant_type + self.variantTypeChanged.emit() @pyqtProperty(str, notify=machineTypeChanged) def machineType(self): @@ -82,8 +83,9 @@ class WorkspaceDialog(QObject): self.machineTypeChanged.emit() def setNumUserSettings(self, num_user_settings): - self._num_user_settings = num_user_settings - self.numVisibleSettingsChanged.emit() + if self._num_user_settings != num_user_settings: + self._num_user_settings = num_user_settings + self.numVisibleSettingsChanged.emit() @pyqtProperty(int, notify=numUserSettingsChanged) def numUserSettings(self): @@ -94,40 +96,45 @@ class WorkspaceDialog(QObject): return self._objects_on_plate def setHasObjectsOnPlate(self, objects_on_plate): - self._objects_on_plate = objects_on_plate - self.objectsOnPlateChanged.emit() + if self._objects_on_plate != objects_on_plate: + self._objects_on_plate = objects_on_plate + self.objectsOnPlateChanged.emit() @pyqtProperty("QVariantList", notify = materialLabelsChanged) def materialLabels(self): return self._material_labels def setMaterialLabels(self, material_labels): - self._material_labels = material_labels - self.materialLabelsChanged.emit() + if self._material_labels != material_labels: + self._material_labels = material_labels + self.materialLabelsChanged.emit() @pyqtProperty("QVariantList", notify=extrudersChanged) def extruders(self): return self._extruders def setExtruders(self, extruders): - self._extruders = extruders - self.extrudersChanged.emit() + if self._extruders != extruders: + self._extruders = extruders + self.extrudersChanged.emit() @pyqtProperty(str, notify = machineNameChanged) def machineName(self): return self._machine_name def setMachineName(self, machine_name): - self._machine_name = machine_name - self.machineNameChanged.emit() + if self._machine_name != machine_name: + self._machine_name = machine_name + self.machineNameChanged.emit() @pyqtProperty(str, notify=qualityTypeChanged) def qualityType(self): return self._quality_type def setQualityType(self, quality_type): - self._quality_type = quality_type - self.qualityTypeChanged.emit() + if self._quality_type != quality_type: + self._quality_type = quality_type + self.qualityTypeChanged.emit() @pyqtProperty(int, notify=numSettingsOverridenByQualityChangesChanged) def numSettingsOverridenByQualityChanges(self): @@ -142,8 +149,9 @@ class WorkspaceDialog(QObject): return self._quality_name def setQualityName(self, quality_name): - self._quality_name = quality_name - self.qualityNameChanged.emit() + if self._quality_name != quality_name: + self._quality_name = quality_name + self.qualityNameChanged.emit() @pyqtProperty(str, notify=activeModeChanged) def activeMode(self): @@ -165,8 +173,9 @@ class WorkspaceDialog(QObject): return self._num_visible_settings def setNumVisibleSettings(self, num_visible_settings): - self._num_visible_settings = num_visible_settings - self.numVisibleSettingsChanged.emit() + if self._num_visible_settings != num_visible_settings: + self._num_visible_settings = num_visible_settings + self.numVisibleSettingsChanged.emit() @pyqtProperty(bool, notify = machineConflictChanged) def machineConflict(self): @@ -191,16 +200,19 @@ class WorkspaceDialog(QObject): Application.getInstance().getBackend().close() def setMaterialConflict(self, material_conflict): - self._has_material_conflict = material_conflict - self.materialConflictChanged.emit() + if self._has_material_conflict != material_conflict: + self._has_material_conflict = material_conflict + self.materialConflictChanged.emit() def setMachineConflict(self, machine_conflict): - self._has_machine_conflict = machine_conflict - self.machineConflictChanged.emit() + if self._has_machine_conflict != machine_conflict: + self._has_machine_conflict = machine_conflict + self.machineConflictChanged.emit() def setQualityChangesConflict(self, quality_changes_conflict): - self._has_quality_changes_conflict = quality_changes_conflict - self.qualityChangesConflictChanged.emit() + if self._has_quality_changes_conflict != quality_changes_conflict: + self._has_quality_changes_conflict = quality_changes_conflict + self.qualityChangesConflictChanged.emit() def getResult(self): if "machine" in self._result and not self._has_machine_conflict: diff --git a/plugins/ChangeLogPlugin/ChangeLog.txt b/plugins/ChangeLogPlugin/ChangeLog.txt index c98f0a7c6b..265a076d25 100644 --- a/plugins/ChangeLogPlugin/ChangeLog.txt +++ b/plugins/ChangeLogPlugin/ChangeLog.txt @@ -98,6 +98,9 @@ Use a mesh to specify a volume within which to classify nothing as overhang for *Delta printer support This release adds support for printers with elliptic buildplates. This feature has not been extensively tested so please let us know if it works or get involved in improving it. +*AppImage for Linux +The Linux distribution is now in AppImage format, which makes Cura easier to install. + *bugfixes The user is now notified when a new version of Cura is available. When searching in the setting visibility preferences, the category for each setting is always displayed. diff --git a/plugins/ImageReader/ConfigUI.qml b/plugins/ImageReader/ConfigUI.qml index 1df57d74c0..893f8e248c 100644 --- a/plugins/ImageReader/ConfigUI.qml +++ b/plugins/ImageReader/ConfigUI.qml @@ -12,11 +12,9 @@ UM.Dialog { width: 350 * Screen.devicePixelRatio; minimumWidth: 350 * Screen.devicePixelRatio; - maximumWidth: 350 * Screen.devicePixelRatio; height: 250 * Screen.devicePixelRatio; minimumHeight: 250 * Screen.devicePixelRatio; - maximumHeight: 250 * Screen.devicePixelRatio; title: catalog.i18nc("@title:window", "Convert Image...") diff --git a/plugins/LayerView/LayerView.py b/plugins/LayerView/LayerView.py index 0d38e89026..15a79c4412 100644 --- a/plugins/LayerView/LayerView.py +++ b/plugins/LayerView/LayerView.py @@ -67,6 +67,7 @@ class LayerView(View): self._show_adhesion = 1 self._show_skin = 1 self._show_infill = 1 + self._legend_items = None Preferences.getInstance().addPreference("view/top_layer_count", 5) Preferences.getInstance().addPreference("view/only_show_top_layers", False) @@ -125,7 +126,7 @@ class LayerView(View): if not self._ghost_shader: self._ghost_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "color.shader")) - self._ghost_shader.setUniformValue("u_color", Color(32, 32, 32, 96)) + self._ghost_shader.setUniformValue("u_color", Color(*Application.getInstance().getTheme().getColor("layerview_ghost").getRgb())) for node in DepthFirstIterator(scene.getRoot()): # We do not want to render ConvexHullNode as it conflicts with the bottom layers. @@ -278,6 +279,9 @@ class LayerView(View): if not self._layerview_composite_shader: self._layerview_composite_shader = OpenGL.getInstance().createShaderProgram(os.path.join(PluginRegistry.getInstance().getPluginPath("LayerView"), "layerview_composite.shader")) + theme = Application.getInstance().getTheme() + self._layerview_composite_shader.setUniformValue("u_background_color", Color(*theme.getColor("viewport_background").getRgb())) + self._layerview_composite_shader.setUniformValue("u_outline_color", Color(*theme.getColor("model_selection_outline").getRgb())) if not self._composite_pass: self._composite_pass = self.getRenderer().getRenderPass("composite") @@ -287,6 +291,8 @@ class LayerView(View): self._old_composite_shader = self._composite_pass.getCompositeShader() self._composite_pass.setCompositeShader(self._layerview_composite_shader) + Application.getInstance().setViewLegendItems(self._getLegendItems()) + elif event.type == Event.ViewDeactivateEvent: self._wireprint_warning_message.hide() Application.getInstance().globalContainerStackChanged.disconnect(self._onGlobalStackChanged) @@ -296,6 +302,8 @@ class LayerView(View): self._composite_pass.setLayerBindings(self._old_layer_bindings) self._composite_pass.setCompositeShader(self._old_composite_shader) + Application.getInstance().setViewLegendItems([]) + def _onGlobalStackChanged(self): if self._global_container_stack: self._global_container_stack.propertyChanged.disconnect(self._onPropertyChanged) @@ -348,6 +356,24 @@ class LayerView(View): self._startUpdateTopLayers() + def _getLegendItems(self): + if self._legend_items is None: + theme = Application.getInstance().getTheme() + self._legend_items = [ + {"color": theme.getColor("layerview_inset_0").name(), "title": catalog.i18nc("@label:layerview polygon type", "Outer Wall")}, # Inset0Type + {"color": theme.getColor("layerview_inset_x").name(), "title": catalog.i18nc("@label:layerview polygon type", "Inner Wall")}, # InsetXType + {"color": theme.getColor("layerview_skin").name(), "title": catalog.i18nc("@label:layerview polygon type", "Top / Bottom")}, # SkinType + {"color": theme.getColor("layerview_infill").name(), "title": catalog.i18nc("@label:layerview polygon type", "Infill")}, # InfillType + {"color": theme.getColor("layerview_support").name(), "title": catalog.i18nc("@label:layerview polygon type", "Support Skin")}, # SupportType + {"color": theme.getColor("layerview_support_infill").name(), "title": catalog.i18nc("@label:layerview polygon type", "Support Infill")}, # SupportInfillType + {"color": theme.getColor("layerview_support_interface").name(), "title": catalog.i18nc("@label:layerview polygon type", "Support Interface")}, # SupportInterfaceType + {"color": theme.getColor("layerview_skirt").name(), "title": catalog.i18nc("@label:layerview polygon type", "Build Plate Adhesion")}, # SkirtType + {"color": theme.getColor("layerview_move_combing").name(), "title": catalog.i18nc("@label:layerview polygon type", "Travel Move")}, # MoveCombingType + {"color": theme.getColor("layerview_move_retraction").name(), "title": catalog.i18nc("@label:layerview polygon type", "Retraction Move")}, # MoveRetractionType + #{"color": theme.getColor("layerview_none").name(), "title": catalog.i18nc("@label:layerview polygon type", "Unknown")} # NoneType + ] + return self._legend_items + class _CreateTopLayersJob(Job): def __init__(self, scene, layer_number, solid_layers): diff --git a/plugins/LayerView/LayerView.qml b/plugins/LayerView/LayerView.qml index de6327e066..92948a7ec6 100644 --- a/plugins/LayerView/LayerView.qml +++ b/plugins/LayerView/LayerView.qml @@ -254,61 +254,5 @@ Item text: "Only color active extruder" } } - - // legend - ListView { - - visible: (UM.LayerView.getLayerViewType() == 1) // line type - anchors.top: view_settings.bottom - anchors.topMargin: UM.Theme.getSize("default_margin").height - //width: parent.width - //height: childrenRect.height - - delegate: Row - { - Rectangle - { - id: rect - - x: UM.Theme.getSize("default_margin").width - y: index * UM.Theme.getSize("section_icon").height - - //width: UM.Theme.getSize("section_icon").width - //height: 0.5 * UM.Theme.getSize("section_icon").height - width: UM.Theme.getSize("setting_control").height / 2 - height: UM.Theme.getSize("setting_control").height / 2 - //Behavior on height { NumberAnimation { duration: 50; } } - - border.width: UM.Theme.getSize("default_lining").width; - border.color: UM.Theme.getColor("slider_groove_border"); - - color: model.color; - } - - Label - { - anchors.left: rect.right - anchors.verticalCenter: rect.verticalCenter - anchors.leftMargin: UM.Theme.getSize("default_margin").width - text: model.label - } - } - model: ListModel - { - id: legendModel - } - Component.onCompleted: - { - // see LayerPolygon - legendModel.append({ label:catalog.i18nc("@label", "Inset0"), color: "#ff0000" }); - legendModel.append({ label:catalog.i18nc("@label", "InsetX"), color: "#00ff00" }); - legendModel.append({ label:catalog.i18nc("@label", "Skin"), color: "#ffff00" }); - legendModel.append({ label:catalog.i18nc("@label", "Support, Skirt, SupportInfill"), color: "#00ffff" }); - legendModel.append({ label:catalog.i18nc("@label", "Infill"), color: "#ffbf00" }); - legendModel.append({ label:catalog.i18nc("@label", "MoveCombing"), color: "#0000ff" }); - legendModel.append({ label:catalog.i18nc("@label", "MoveRetraction"), color: "#8080ff" }); - legendModel.append({ label:catalog.i18nc("@label", "SupportInterface"), color: "#3fbfff" }); - } - } } } diff --git a/plugins/SolidView/SolidView.py b/plugins/SolidView/SolidView.py index 8277813c92..893c4ed180 100644 --- a/plugins/SolidView/SolidView.py +++ b/plugins/SolidView/SolidView.py @@ -38,8 +38,9 @@ class SolidView(View): if not self._disabled_shader: self._disabled_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "striped.shader")) - self._disabled_shader.setUniformValue("u_diffuseColor1", [0.48, 0.48, 0.48, 1.0]) - self._disabled_shader.setUniformValue("u_diffuseColor2", [0.68, 0.68, 0.68, 1.0]) + theme = Application.getInstance().getTheme() + self._disabled_shader.setUniformValue("u_diffuseColor1", theme.getColor("model_unslicable").getRgbF()) + self._disabled_shader.setUniformValue("u_diffuseColor2", theme.getColor("model_unslicable_alt").getRgbF()) self._disabled_shader.setUniformValue("u_width", 50.0) multi_extrusion = False diff --git a/plugins/XRayView/XRayView.py b/plugins/XRayView/XRayView.py index 9913ee786f..931ecb1975 100644 --- a/plugins/XRayView/XRayView.py +++ b/plugins/XRayView/XRayView.py @@ -3,6 +3,8 @@ import os.path +from UM.Application import Application +from UM.Math.Color import Color from UM.PluginRegistry import PluginRegistry from UM.Event import Event from UM.View.View import View @@ -31,7 +33,7 @@ class XRayView(View): if not self._xray_shader: self._xray_shader = OpenGL.getInstance().createShaderProgram(os.path.join(PluginRegistry.getInstance().getPluginPath("XRayView"), "xray.shader")) - self._xray_shader.setUniformValue("u_color", [0.1, 0.1, 0.2, 1.0]) + self._xray_shader.setUniformValue("u_color", Color(*Application.getInstance().getTheme().getColor("xray").getRgb())) for node in BreadthFirstIterator(scene.getRoot()): if not node.render(renderer): @@ -58,6 +60,10 @@ class XRayView(View): if not self._xray_composite_shader: self._xray_composite_shader = OpenGL.getInstance().createShaderProgram(os.path.join(PluginRegistry.getInstance().getPluginPath("XRayView"), "xray_composite.shader")) + theme = Application.getInstance().getTheme() + self._xray_composite_shader.setUniformValue("u_background_color", Color(*theme.getColor("viewport_background").getRgb())) + self._xray_composite_shader.setUniformValue("u_error_color", Color(*theme.getColor("xray_error").getRgb())) + self._xray_composite_shader.setUniformValue("u_outline_color", Color(*theme.getColor("model_selection_outline").getRgb())) if not self._composite_pass: self._composite_pass = self.getRenderer().getRenderPass("composite") diff --git a/plugins/XRayView/xray_composite.shader b/plugins/XRayView/xray_composite.shader index f6e67c1d0f..e7a38950bf 100644 --- a/plugins/XRayView/xray_composite.shader +++ b/plugins/XRayView/xray_composite.shader @@ -22,6 +22,7 @@ fragment = uniform float u_outline_strength; uniform vec4 u_outline_color; uniform vec4 u_error_color; + uniform vec4 u_background_color; const vec3 x_axis = vec3(1.0, 0.0, 0.0); const vec3 y_axis = vec3(0.0, 1.0, 0.0); @@ -37,7 +38,7 @@ fragment = kernel[3] = 1.0; kernel[4] = -4.0; kernel[5] = 1.0; kernel[6] = 0.0; kernel[7] = 1.0; kernel[8] = 0.0; - vec4 result = vec4(0.965, 0.965, 0.965, 1.0); + vec4 result = u_background_color; vec4 layer0 = texture2D(u_layer0, v_uvs); result = layer0 * layer0.a + result * (1.0 - layer0.a); @@ -70,6 +71,7 @@ fragment = u_layer0 = 0 u_layer1 = 1 u_layer2 = 2 +u_background_color = [0.965, 0.965, 0.965, 1.0] u_outline_strength = 1.0 u_outline_color = [0.05, 0.66, 0.89, 1.0] u_error_color = [1.0, 0.0, 0.0, 1.0] diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 6627754274..14f65137e9 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -1230,6 +1230,16 @@ "default_value": true, "enabled": "infill_sparse_density > 0", "settable_per_mesh": true + }, + "min_infill_area": + { + "label": "Minimum Infill Area", + "description": "Don't generate areas of infill smaller than this (use skin instead).", + "unit": "mm²", + "type": "float", + "minimum_value": "0", + "default_value": 0, + "settable_per_mesh": true } } }, diff --git a/resources/definitions/kossel_pro.def.json b/resources/definitions/kossel_pro.def.json index a0774b3a06..e8403a1727 100644 --- a/resources/definitions/kossel_pro.def.json +++ b/resources/definitions/kossel_pro.def.json @@ -45,7 +45,7 @@ "default_value": "RepRap (Marlin/Sprinter)" }, "machine_start_gcode": { - "default_value": "; info: M303 E0 S200 C8 ; Pid auto-tune \n\nM140 S{{material_bed_temperature}}; Start heating up the base\nG28 ; Home to top 3 endstops\n; Autolevel and adjust first layer\n; Adjust this value to fit your own printer! (positive is thicker)\n; This default value is intentionally very high to accommodate the\n; variety of print heads used with this printer. Many of you will\n; need tiny values like Z0 or Z0.1. Use feeler gauges to dial this\n; in as accurately as possible.\nG29 Z10\n\n; Squirt and wipe ;\nM109 S220 ; Wait for the temp to hit 220\nG00 X125 Y-60 Z0.1 ;\nG92 E0 ;\nG01 E25 F100 ; Extrude a little bit to replace oozage from auto levelling\nG01 X90 Y-50 F6000 ;\nG01 Z5 ;\n\n; Set the extruder to the requested print temperature\nM104 S{{material_print_temperature}}\n" + "default_value": "; info: M303 E0 S200 C8 ; Pid auto-tune \n\nM140 S{material_bed_temperature}; Start heating up the base\nG28 ; Home to top 3 endstops\n; Autolevel and adjust first layer\n; Adjust this value to fit your own printer! (positive is thicker)\n; This default value is intentionally very high to accommodate the\n; variety of print heads used with this printer. Many of you will\n; need tiny values like Z0 or Z0.1. Use feeler gauges to dial this\n; in as accurately as possible.\nG29 Z10\n\n; Squirt and wipe ;\nM109 S220 ; Wait for the temp to hit 220\nG00 X125 Y-60 Z0.1 ;\nG92 E0 ;\nG01 E25 F100 ; Extrude a little bit to replace oozage from auto levelling\nG01 X90 Y-50 F6000 ;\nG01 Z5 ;\n\n; Set the extruder to the requested print temperature\nM104 S{material_print_temperature}\n" }, "machine_end_gcode": { "default_value": "M104 S0 ; turn off temperature\nM140 S0 ; turn off bed\nG28 ; home all axes\nM84 ; disable motors\n" @@ -54,4 +54,4 @@ "default_value": "elliptic" } } -} \ No newline at end of file +} diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 1383338144..264bec6d9a 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -306,6 +306,18 @@ UM.MainWindow } } + Legend + { + id: legend + anchors + { + top: parent.top + topMargin: UM.Theme.getSize("default_margin").height + right: sidebar.left + rightMargin: UM.Theme.getSize("default_margin").width + } + } + JobSpecs { id: jobSpecs diff --git a/resources/qml/JobSpecs.qml b/resources/qml/JobSpecs.qml index 78f184f13c..00d22ae8a8 100644 --- a/resources/qml/JobSpecs.qml +++ b/resources/qml/JobSpecs.qml @@ -26,6 +26,7 @@ Rectangle { property variant printDuration: PrintInformation.currentPrintTime property variant printMaterialLengths: PrintInformation.materialLengths property variant printMaterialWeights: PrintInformation.materialWeights + property variant printMaterialCosts: PrintInformation.materialCosts height: childrenRect.height color: "transparent" @@ -133,7 +134,8 @@ Rectangle { } } - Label{ + Label + { id: boundingSpec anchors.top: jobNameRow.bottom anchors.right: parent.right @@ -144,17 +146,20 @@ Rectangle { text: Printer.getSceneBoundingBoxString } - Rectangle { + Rectangle + { id: specsRow anchors.top: boundingSpec.bottom anchors.right: parent.right height: UM.Theme.getSize("jobspecs_line").height - Item{ + Item + { width: parent.width height: parent.height - UM.RecolorImage { + UM.RecolorImage + { id: timeIcon anchors.right: timeSpec.left anchors.rightMargin: UM.Theme.getSize("default_margin").width/2 @@ -166,7 +171,8 @@ Rectangle { color: UM.Theme.getColor("text_subtext") source: UM.Theme.getIcon("print_time") } - Label{ + Label + { id: timeSpec anchors.right: lengthIcon.left anchors.rightMargin: UM.Theme.getSize("default_margin").width @@ -175,7 +181,8 @@ Rectangle { color: UM.Theme.getColor("text_subtext") text: (!base.printDuration || !base.printDuration.valid) ? catalog.i18nc("@label", "00h 00min") : base.printDuration.getDisplayString(UM.DurationFormat.Short) } - UM.RecolorImage { + UM.RecolorImage + { id: lengthIcon anchors.right: lengthSpec.left anchors.rightMargin: UM.Theme.getSize("default_margin").width/2 @@ -187,7 +194,8 @@ Rectangle { color: UM.Theme.getColor("text_subtext") source: UM.Theme.getIcon("category_material") } - Label{ + Label + { id: lengthSpec anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter @@ -197,19 +205,38 @@ Rectangle { { var lengths = []; var weights = []; + var costs = []; + var someCostsKnown = false; if(base.printMaterialLengths) { - for(var index = 0; index < base.printMaterialLengths.length; index++) { - if(base.printMaterialLengths[index] > 0) { + for(var index = 0; index < base.printMaterialLengths.length; index++) + { + if(base.printMaterialLengths[index] > 0) + { lengths.push(base.printMaterialLengths[index].toFixed(2)); weights.push(String(Math.floor(base.printMaterialWeights[index]))); + costs.push(base.printMaterialCosts[index].toFixed(2)); + if(base.printMaterialCosts[index] > 0) + { + someCostsKnown = true; + } } } } - if(lengths.length == 0) { + if(lengths.length == 0) + { lengths = ["0.00"]; weights = ["0"]; + costs = ["0.00"]; + } + if(someCostsKnown) + { + return catalog.i18nc("@label", "%1 m / ~ %2 g / ~ %4 %3").arg(lengths.join(" + ")) + .arg(weights.join(" + ")).arg(costs.join(" + ")).arg(UM.Preferences.getValue("cura/currency")); + } + else + { + return catalog.i18nc("@label", "%1 m / ~ %2 g").arg(lengths.join(" + ")).arg(weights.join(" + ")); } - return catalog.i18nc("@label", "%1 m / ~ %2 g").arg(lengths.join(" + ")).arg(weights.join(" + ")); } } } diff --git a/resources/qml/Legend.qml b/resources/qml/Legend.qml new file mode 100644 index 0000000000..353747ef67 --- /dev/null +++ b/resources/qml/Legend.qml @@ -0,0 +1,66 @@ +// Copyright (c) 2015 Ultimaker B.V. +// Cura is released under the terms of the AGPLv3 or higher. + +import QtQuick 2.2 +import QtQuick.Controls 1.1 +import QtQuick.Controls.Styles 1.1 +import QtQuick.Layouts 1.1 + +import UM 1.1 as UM +import Cura 1.0 as Cura + +Rectangle { + id: base + + UM.I18nCatalog { id: catalog; name:"cura"} + + width: childrenRect.width + height: childrenRect.height + color: "transparent" + + Connections + { + target: Printer + onViewLegendItemsChanged: + { + legendItemRepeater.model = items + } + } + + Column + { + Repeater + { + id: legendItemRepeater + + Item { + anchors.right: parent.right + height: childrenRect.height + width: childrenRect.width + + Rectangle { + id: swatch + + anchors.right: parent.right + anchors.verticalCenter: label.verticalCenter + height: UM.Theme.getSize("setting_control").height / 2 + width: height + + color: modelData.color + border.width: UM.Theme.getSize("default_lining").width + border.color: UM.Theme.getColor("text_subtext") + } + Label { + id: label + + text: modelData.title + font: UM.Theme.getFont("small") + color: UM.Theme.getColor("text_subtext") + + anchors.right: swatch.left + anchors.rightMargin: UM.Theme.getSize("default_margin").width / 2 + } + } + } + } +} diff --git a/resources/qml/Preferences/GeneralPage.qml b/resources/qml/Preferences/GeneralPage.qml index 57a35943d9..838d94e487 100644 --- a/resources/qml/Preferences/GeneralPage.qml +++ b/resources/qml/Preferences/GeneralPage.qml @@ -129,6 +129,19 @@ UM.PreferencesPage currentIndex -= 1; } } + + Label + { + id: currencyLabel + text: catalog.i18nc("@label","Currency:") + anchors.verticalCenter: languageComboBox.verticalCenter + } + TextField + { + id: currencyField + text: UM.Preferences.getValue("cura/currency") + onTextChanged: UM.Preferences.setValue("cura/currency", text) + } } Label diff --git a/resources/qml/Preferences/MaterialView.qml b/resources/qml/Preferences/MaterialView.qml index 7ea363454b..17f76466ab 100644 --- a/resources/qml/Preferences/MaterialView.qml +++ b/resources/qml/Preferences/MaterialView.qml @@ -15,14 +15,19 @@ TabView property QtObject properties; property bool editingEnabled: false; - property string currency: UM.Preferences.getValue("general/currency") ? UM.Preferences.getValue("general/currency") : "€" + property string currency: UM.Preferences.getValue("cura/currency") ? UM.Preferences.getValue("cura/currency") : "€" property real firstColumnWidth: width * 0.45 property real secondColumnWidth: width * 0.45 property string containerId: "" + property var materialPreferenceValues: UM.Preferences.getValue("cura/material_settings") ? JSON.parse(UM.Preferences.getValue("cura/material_settings")) : {} + + property double spoolLength: calculateSpoolLength() + property real costPerMeter: calculateCostPerMeter() Tab { title: catalog.i18nc("@title","Information") + anchors { leftMargin: UM.Theme.getSize("default_margin").width @@ -35,6 +40,7 @@ TabView { anchors.fill: parent horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff + flickableItem.flickableDirection: Flickable.VerticalFlick Flow { @@ -112,64 +118,78 @@ TabView Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Density") } ReadOnlySpinBox { - width: base.secondColumnWidth; - value: properties.density; + id: densitySpinBox + width: base.secondColumnWidth + value: properties.density decimals: 2 - suffix: "g/cm³" + suffix: " g/cm³" stepSize: 0.01 - readOnly: !base.editingEnabled; + readOnly: !base.editingEnabled onEditingFinished: base.setMetaDataEntry("properties/density", properties.density, value) + onValueChanged: updateCostPerMeter() } Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Diameter") } ReadOnlySpinBox { - width: base.secondColumnWidth; - value: properties.diameter; + id: diameterSpinBox + width: base.secondColumnWidth + value: properties.diameter decimals: 2 - suffix: "mm" + suffix: " mm" stepSize: 0.01 - readOnly: !base.editingEnabled; + readOnly: !base.editingEnabled onEditingFinished: base.setMetaDataEntry("properties/diameter", properties.diameter, value) + onValueChanged: updateCostPerMeter() } Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Filament Cost") } SpinBox { - width: base.secondColumnWidth; - value: properties.spool_cost; - prefix: base.currency - enabled: false + id: spoolCostSpinBox + width: base.secondColumnWidth + value: base.getMaterialPreferenceValue(properties.guid, "spool_cost") + prefix: base.currency + " " + decimals: 2 + maximumValue: 1000 + + onEditingFinished: base.setMaterialPreferenceValue(properties.guid, "spool_cost", parseFloat(value)) + onValueChanged: updateCostPerMeter() } Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Filament weight") } SpinBox { - width: base.secondColumnWidth; - value: properties.spool_weight; - suffix: "g"; - stepSize: 10 - enabled: false + id: spoolWeightSpinBox + width: base.secondColumnWidth + value: base.getMaterialPreferenceValue(properties.guid, "spool_weight") + suffix: " g" + stepSize: 100 + decimals: 0 + maximumValue: 10000 + + onEditingFinished: base.setMaterialPreferenceValue(properties.guid, "spool_weight", parseFloat(value)) + onValueChanged: updateCostPerMeter() } Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Filament length") } - SpinBox + Label { - width: base.secondColumnWidth; - value: parseFloat(properties.spool_length); - suffix: "m"; - enabled: false + width: base.secondColumnWidth + text: "~ %1 m".arg(Math.round(base.spoolLength)) + verticalAlignment: Qt.AlignVCenter + height: parent.rowHeight } - Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Cost per Meter (Approx.)") } - SpinBox + Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Cost per Meter") } + Label { - width: base.secondColumnWidth; - value: parseFloat(properties.cost_per_meter); - suffix: catalog.i18nc("@label", "%1/m".arg(base.currency)); - enabled: false + width: base.secondColumnWidth + text: "~ %1 %2/m".arg(base.costPerMeter.toFixed(2)).arg(base.currency) + verticalAlignment: Qt.AlignVCenter + height: parent.rowHeight } Item { width: parent.width; height: UM.Theme.getSize("default_margin").height } @@ -200,6 +220,12 @@ TabView onEditingFinished: base.setMetaDataEntry("adhesion_info", properties.adhesion_info, text) } } + + function updateCostPerMeter() + { + base.spoolLength = calculateSpoolLength(diameterSpinBox.value, densitySpinBox.value, spoolWeightSpinBox.value); + base.costPerMeter = calculateCostPerMeter(spoolCostSpinBox.value); + } } } @@ -259,6 +285,44 @@ TabView } } + function calculateSpoolLength(diameter, density, spoolWeight) + { + if(!diameter) + { + diameter = properties.diameter; + } + if(!density) + { + density = properties.density; + } + if(!spoolWeight) + { + spoolWeight = base.getMaterialPreferenceValue(properties.guid, "spool_weight"); + } + + if (diameter == 0 || density == 0 || spoolWeight == 0) + { + return 0; + } + var area = Math.PI * Math.pow(diameter / 2, 2); // in mm2 + var volume = (spoolWeight / density); // in cm3 + return volume / area; // in m + } + + function calculateCostPerMeter(spoolCost) + { + if(!spoolCost) + { + spoolCost = base.getMaterialPreferenceValue(properties.guid, "spool_cost"); + } + + if (spoolLength == 0) + { + return 0; + } + return spoolCost / spoolLength; + } + // Tiny convenience function to check if a value really changed before trying to set it. function setMetaDataEntry(entry_name, old_value, new_value) { @@ -268,6 +332,32 @@ TabView } } + function setMaterialPreferenceValue(material_guid, entry_name, new_value) + { + if(!(material_guid in materialPreferenceValues)) + { + materialPreferenceValues[material_guid] = {}; + } + if(entry_name in materialPreferenceValues[material_guid] && materialPreferenceValues[material_guid][entry_name] == new_value) + { + // value has not changed + return + } + materialPreferenceValues[material_guid][entry_name] = new_value; + + // store preference + UM.Preferences.setValue("cura/material_settings", JSON.stringify(materialPreferenceValues)); + } + + function getMaterialPreferenceValue(material_guid, entry_name) + { + if(material_guid in materialPreferenceValues && entry_name in materialPreferenceValues[material_guid]) + { + return materialPreferenceValues[material_guid][entry_name]; + } + return 0; + } + function setName(old_value, new_value) { if(old_value != new_value) diff --git a/resources/qml/Preferences/MaterialsPage.qml b/resources/qml/Preferences/MaterialsPage.qml index 264bc182e6..6072541976 100644 --- a/resources/qml/Preferences/MaterialsPage.qml +++ b/resources/qml/Preferences/MaterialsPage.qml @@ -185,17 +185,6 @@ UM.ManagementPage height: childrenRect.height Label { text: materialProperties.name; font: UM.Theme.getFont("large"); } - Button - { - id: editButton - anchors.right: parent.right; - text: catalog.i18nc("@action:button", "Edit"); - iconName: "document-edit"; - - enabled: base.currentItem != null && !base.currentItem.readOnly - - checkable: enabled - } } MaterialView @@ -209,7 +198,7 @@ UM.ManagementPage bottom: parent.bottom } - editingEnabled: editButton.checkable && editButton.checked; + editingEnabled: base.currentItem != null && !base.currentItem.readOnly properties: materialProperties containerId: base.currentItem != null ? base.currentItem.id : "" @@ -219,6 +208,7 @@ UM.ManagementPage { id: materialProperties + property string guid: "00000000-0000-0000-0000-000000000000" property string name: "Unknown"; property string profile_type: "Unknown"; property string supplier: "Unknown"; @@ -344,6 +334,7 @@ UM.ManagementPage return } materialProperties.name = currentItem.name; + materialProperties.guid = Cura.ContainerManager.getContainerMetaDataEntry(base.currentItem.id, "GUID"); if(currentItem.metadata != undefined && currentItem.metadata != null) { diff --git a/resources/qml/WorkspaceSummaryDialog.qml b/resources/qml/WorkspaceSummaryDialog.qml index 2072ddb1e7..4c0a7bf86d 100644 --- a/resources/qml/WorkspaceSummaryDialog.qml +++ b/resources/qml/WorkspaceSummaryDialog.qml @@ -15,11 +15,10 @@ UM.Dialog width: 550 minimumWidth: 550 - maximumWidth: 550 height: 350 minimumHeight: 350 - maximumHeight: 350 + property int spacerHeight: 10 property bool dontShowAgain: true diff --git a/resources/themes/cura/theme.json b/resources/themes/cura/theme.json index d5a95a7104..31caeeabd4 100644 --- a/resources/themes/cura/theme.json +++ b/resources/themes/cura/theme.json @@ -192,7 +192,44 @@ "status_ready": [0, 205, 0, 255], "status_busy": [12, 169, 227, 255], "status_paused": [255, 140, 0, 255], - "status_stopped": [236, 82, 80, 255] + "status_stopped": [236, 82, 80, 255], + + "disabled_axis": [127, 127, 127, 255], + "x_axis": [255, 0, 0, 255], + "y_axis": [0, 0, 255, 255], + "z_axis": [0, 255, 0, 255], + "all_axis": [255, 255, 255, 255], + + "viewport_background": [245, 245, 245, 255], + "volume_outline": [12, 169, 227, 255], + "buildplate": [244, 244, 244, 255], + "buildplate_alt": [204, 204, 204, 255], + + "convex_hull": [35, 35, 35, 127], + "disallowed_area": [0, 0, 0, 40], + "error_area": [255, 0, 0, 127], + + "model_default": [255, 201, 36, 255], + "model_overhang": [255, 0, 0, 255], + "model_unslicable": [122, 122, 122, 255], + "model_unslicable_alt": [172, 172, 127, 255], + "model_selection_outline": [12, 169, 227, 255], + + "xray": [26, 26, 62, 255], + "xray_error": [255, 0, 0, 255], + + "layerview_ghost": [32, 32, 32, 96], + "layerview_none": [255, 255, 255, 255], + "layerview_inset_0": [255, 0, 0, 255], + "layerview_inset_x": [0, 255, 0, 255], + "layerview_skin": [255, 255, 0, 255], + "layerview_support": [0, 255, 255, 255], + "layerview_skirt": [0, 255, 255, 255], + "layerview_infill": [255, 192, 0, 255], + "layerview_support_infill": [0, 255, 255, 255], + "layerview_move_combing": [0, 0, 255, 255], + "layerview_move_retraction": [128, 128, 255, 255], + "layerview_support_interface": [64, 192, 255, 255] }, "sizes": {