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 @@
+
+
\ 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 @@
+
+
\ 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 @@
+
+
\ 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 @@
+
+
\ 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 @@
+
+
\ 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 {