diff --git a/.gitignore b/.gitignore index ac1e8eba92..f67add62cf 100644 --- a/.gitignore +++ b/.gitignore @@ -44,6 +44,7 @@ plugins/cura-god-mode-plugin plugins/cura-big-flame-graph plugins/cura-siemensnx-plugin plugins/CuraVariSlicePlugin +plugins/CuraLiveScriptingPlugin #Build stuff CMakeCache.txt diff --git a/cura/PrintInformation.py b/cura/PrintInformation.py index 3cced2f85c..f0c29f5b81 100644 --- a/cura/PrintInformation.py +++ b/cura/PrintInformation.py @@ -70,6 +70,7 @@ class PrintInformation(QObject): Application.getInstance().globalContainerStackChanged.connect(self._updateJobName) Application.getInstance().fileLoaded.connect(self.setBaseName) + Application.getInstance().workspaceLoaded.connect(self.setProjectName) Preferences.getInstance().preferenceChanged.connect(self._onPreferencesChanged) self._active_material_container = None @@ -283,7 +284,11 @@ class PrintInformation(QObject): return self._base_name @pyqtSlot(str) - def setBaseName(self, base_name): + def setProjectName(self, name): + self.setBaseName(name, is_project_file = True) + + @pyqtSlot(str) + def setBaseName(self, base_name, is_project_file = False): # Ensure that we don't use entire path but only filename name = os.path.basename(base_name) @@ -291,15 +296,17 @@ class PrintInformation(QObject): # extension. This cuts the extension off if necessary. name = os.path.splitext(name)[0] + # if this is a profile file, always update the job name # name is "" when I first had some meshes and afterwards I deleted them so the naming should start again is_empty = name == "" - if is_empty or (self._base_name == "" and self._base_name != name): + if is_project_file or (is_empty or (self._base_name == "" and self._base_name != name)): # remove ".curaproject" suffix from (imported) the file name if name.endswith(".curaproject"): name = name[:name.rfind(".curaproject")] self._base_name = name self._updateJobName() + ## Created an acronymn-like abbreviated machine name from the currently active machine name # Called each time the global stack is switched def _setAbbreviatedMachineName(self): diff --git a/cura/Settings/CuraContainerRegistry.py b/cura/Settings/CuraContainerRegistry.py index 7abe5b35f5..1674405824 100644 --- a/cura/Settings/CuraContainerRegistry.py +++ b/cura/Settings/CuraContainerRegistry.py @@ -409,7 +409,7 @@ class CuraContainerRegistry(ContainerRegistry): extruder_stack = None # if extruders are defined in the machine definition use those instead - if machine.extruders and len(machine.extruders) > 0: + if machine.extruders and "0" in machine.extruders: new_extruder_id = machine.extruders["0"].getId() extruder_stack = machine.extruders["0"] @@ -449,15 +449,16 @@ class CuraContainerRegistry(ContainerRegistry): extruder_stack.setVariantById(variant_id) extruder_stack.setMaterialById("default") extruder_stack.setQualityById("default") - quality_changes_id = "default" if machine.qualityChanges.getId() != "empty_quality_changes": extruder_quality_changes_container = self.findInstanceContainers(name = machine.qualityChanges.getName(), extruder = extruder_id) if extruder_quality_changes_container: quality_changes_id = extruder_quality_changes_container[0].getId() - extruder_stack.setQualityChangesById(quality_changes_id) + extruder_stack.setQualityChangesById(quality_changes_id) self.addContainer(extruder_stack) + return extruder_stack + # Fix the extruders that were upgraded to ExtruderStack instances during addContainer. # The stacks are now responsible for setting the next stack on deserialize. However, # due to problems with loading order, some stacks may not have the proper next stack diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 002c84fb67..ca929b46fc 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -621,9 +621,16 @@ class MachineManager(QObject): def activeQualityId(self) -> str: if self._active_container_stack: quality = self._active_container_stack.quality + if isinstance(quality, type(self._empty_quality_container)): + return "" quality_changes = self._active_container_stack.qualityChanges - if quality and quality_changes and isinstance(quality_changes, type(self._empty_quality_changes_container)) and not isinstance(quality, type(self._empty_quality_container)): - return quality.getId() + if quality and quality_changes: + if isinstance(quality_changes, type(self._empty_quality_changes_container)): + # It's a built-in profile + return quality.getId() + else: + # Custom profile + return quality_changes.getId() return "" @pyqtProperty(str, notify=activeQualityChanged) diff --git a/plugins/3MFReader/ThreeMFWorkspaceReader.py b/plugins/3MFReader/ThreeMFWorkspaceReader.py index a237460bab..aa81399b56 100755 --- a/plugins/3MFReader/ThreeMFWorkspaceReader.py +++ b/plugins/3MFReader/ThreeMFWorkspaceReader.py @@ -746,7 +746,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader): # If not extruder stacks were saved in the project file (pre 3.1) create one manually # We re-use the container registry's addExtruderStackForSingleExtrusionMachine method for this if not extruder_stacks: - self._container_registry.addExtruderStackForSingleExtrusionMachine(global_stack, "fdmextruder") + extruder_stacks.append(self._container_registry.addExtruderStackForSingleExtrusionMachine(global_stack, "fdmextruder")) except: Logger.logException("w", "We failed to serialize the stack. Trying to clean up.") diff --git a/resources/qml/Topbar.qml b/resources/qml/Topbar.qml index c69c786d5a..033e1a55f9 100644 --- a/resources/qml/Topbar.qml +++ b/resources/qml/Topbar.qml @@ -220,6 +220,81 @@ Rectangle menu: PrinterMenu { } } + //View orientation Item + Row + { + id: viewOrientationControl + height: 30 + + spacing: 2 + + anchors { + verticalCenter: base.verticalCenter + right: viewModeButton.right + rightMargin: UM.Theme.getSize("default_margin").width + viewModeButton.width + } + + // #1 3d view + Button + { + iconSource: UM.Theme.getIcon("view_3d") + style: UM.Theme.styles.orientation_button + anchors.verticalCenter: viewOrientationControl.verticalCenter + onClicked:{ + UM.Controller.rotateView("3d", 0); + } + visible: base.width > 1100 + } + + // #2 Front view + Button + { + iconSource: UM.Theme.getIcon("view_front") + style: UM.Theme.styles.orientation_button + anchors.verticalCenter: viewOrientationControl.verticalCenter + onClicked:{ + UM.Controller.rotateView("home", 0); + } + visible: base.width > 1130 + } + + // #3 Top view + Button + { + iconSource: UM.Theme.getIcon("view_top") + style: UM.Theme.styles.orientation_button + anchors.verticalCenter: viewOrientationControl.verticalCenter + onClicked:{ + UM.Controller.rotateView("y", 90); + } + visible: base.width > 1160 + } + + // #4 Left view + Button + { + iconSource: UM.Theme.getIcon("view_left") + style: UM.Theme.styles.orientation_button + anchors.verticalCenter: viewOrientationControl.verticalCenter + onClicked:{ + UM.Controller.rotateView("x", 90); + } + visible: base.width > 1190 + } + + // #5 Left view + Button + { + iconSource: UM.Theme.getIcon("view_right") + style: UM.Theme.styles.orientation_button + anchors.verticalCenter: viewOrientationControl.verticalCenter + onClicked:{ + UM.Controller.rotateView("x", -90); + } + visible: base.width > 1210 + } + } + ComboBox { id: viewModeButton diff --git a/resources/themes/cura-light/icons/view_3d.svg b/resources/themes/cura-light/icons/view_3d.svg new file mode 100644 index 0000000000..cfe394e65d --- /dev/null +++ b/resources/themes/cura-light/icons/view_3d.svg @@ -0,0 +1,14 @@ + + + + icn_perspectives_white + Created with Sketch. + + + + + + + + + \ No newline at end of file diff --git a/resources/themes/cura-light/icons/view_front.svg b/resources/themes/cura-light/icons/view_front.svg new file mode 100644 index 0000000000..7de9abe0af --- /dev/null +++ b/resources/themes/cura-light/icons/view_front.svg @@ -0,0 +1,19 @@ + + + + icn_front_white + Created with Sketch. + + + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/themes/cura-light/icons/view_left.svg b/resources/themes/cura-light/icons/view_left.svg new file mode 100644 index 0000000000..1770da4c81 --- /dev/null +++ b/resources/themes/cura-light/icons/view_left.svg @@ -0,0 +1,21 @@ + + + + icn_left_white + Created with Sketch. + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/themes/cura-light/icons/view_right.svg b/resources/themes/cura-light/icons/view_right.svg new file mode 100644 index 0000000000..5e0628e60e --- /dev/null +++ b/resources/themes/cura-light/icons/view_right.svg @@ -0,0 +1,21 @@ + + + + icn_right_white + Created with Sketch. + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/themes/cura-light/icons/view_top.svg b/resources/themes/cura-light/icons/view_top.svg new file mode 100644 index 0000000000..3eb32e9878 --- /dev/null +++ b/resources/themes/cura-light/icons/view_top.svg @@ -0,0 +1,21 @@ + + + + icn_top_white + Created with Sketch. + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/themes/cura-light/styles.qml b/resources/themes/cura-light/styles.qml index ea9d184926..0f3c910270 100755 --- a/resources/themes/cura-light/styles.qml +++ b/resources/themes/cura-light/styles.qml @@ -381,6 +381,111 @@ QtObject { } } + property Component orientation_button: Component { + ButtonStyle { + background: Item { + implicitWidth: 25; + implicitHeight: 25; + + Rectangle { + id: buttonFace2; + + anchors.fill: parent; + property bool down: control.pressed || (control.checkable && control.checked); + + color: { + if(control.customColor !== undefined && control.customColor !== null) { + return control.customColor + } else if(control.checkable && control.checked && control.hovered) { + return Theme.getColor("button_active_hover"); + } else if(control.pressed || (control.checkable && control.checked)) { + return Theme.getColor("button_active"); + } else if(control.hovered) { + return Theme.getColor("button_hover"); + } else { + //return Theme.getColor("button"); + return "transparent" + } + } + Behavior on color { ColorAnimation { duration: 50; } } + + border.width: (control.hasOwnProperty("needBorder") && control.needBorder) ? 2 * screenScaleFactor : 0 + border.color: Theme.getColor("tool_button_border") + + UM.RecolorImage { + id: tool_button_arrow2 + //anchors.right: parent.right; + //anchors.rightMargin: (Theme.getSize("button").width - Theme.getSize("button_icon").width) / 4 + //anchors.bottom: parent.bottom; + //anchors.bottomMargin: (Theme.getSize("button").height - Theme.getSize("button_icon").height) / 4 + //width: Theme.getSize("standard_arrow").width + //height: Theme.getSize("standard_arrow").height + + width: 5 + height: 5 + + sourceSize.width: 5 + sourceSize.height: 5 + visible: control.menu != null; + color: + { + if(control.checkable && control.checked && control.hovered) + { + return Theme.getColor("button_text_active_hover"); + } + else if(control.pressed || (control.checkable && control.checked)) + { + return Theme.getColor("button_text_active"); + } + else if(control.hovered) + { + return Theme.getColor("button_text_hover"); + } + else + { + return Theme.getColor("button_text"); + } + } + source: Theme.getIcon("arrow_bottom") + } + } + } + + label: Item { + UM.RecolorImage { + anchors.centerIn: parent; + opacity: !control.enabled ? 0.2 : 1.0 + source: control.iconSource; + width: 20; + height: 20; + color: + { + if(control.checkable && control.checked && control.hovered) + { + return Theme.getColor("button_text_active_hover"); + } + else if(control.pressed || (control.checkable && control.checked)) + { + return Theme.getColor("button_text_active"); + } + else if(control.hovered) + { + //return Theme.getColor("button_text_hover"); + return "white" + } + else + { + //return Theme.getColor("button_text"); + return "black" + } + } + + sourceSize: Theme.getSize("button_icon") + } + } + } + } + property Component progressbar: Component{ ProgressBarStyle { background: Rectangle { @@ -753,6 +858,49 @@ QtObject { } } + property Component partially_checkbox: Component { + CheckBoxStyle { + background: Item { } + indicator: Rectangle { + implicitWidth: Theme.getSize("checkbox").width; + implicitHeight: Theme.getSize("checkbox").height; + + color: (control.hovered || control._hovered) ? Theme.getColor("checkbox_hover") : Theme.getColor("checkbox"); + Behavior on color { ColorAnimation { duration: 50; } } + + radius: control.exclusiveGroup ? Theme.getSize("checkbox").width / 2 : 0 + + border.width: Theme.getSize("default_lining").width; + border.color: (control.hovered || control._hovered) ? Theme.getColor("checkbox_border_hover") : Theme.getColor("checkbox_border"); + + UM.RecolorImage { + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width / 2.5 + height: parent.height / 2.5 + sourceSize.width: width + sourceSize.height: width + color: Theme.getColor("checkbox_mark") + source: { + if (control.checkbox_state == 2){ + return Theme.getIcon("solid") + } + else{ + return control.exclusiveGroup ? Theme.getIcon("dot") : Theme.getIcon("check") + } + } + opacity: control.checked + Behavior on opacity { NumberAnimation { duration: 100; } } + } + } + label: Label { + text: control.text; + color: Theme.getColor("checkbox_text"); + font: Theme.getFont("default"); + } + } + } + property Component slider: Component { SliderStyle { groove: Rectangle {