diff --git a/meshroom/core/graph.py b/meshroom/core/graph.py
index e63aceca1a..7808f1e235 100644
--- a/meshroom/core/graph.py
+++ b/meshroom/core/graph.py
@@ -215,6 +215,7 @@ def __init__(self, name, parent=None):
super(Graph, self).__init__(parent)
self.name = name
self._loading = False
+ self._saving = False
self._updateEnabled = True
self._updateRequested = False
self.dirtyTopology = False
@@ -251,6 +252,11 @@ def fileFeatures(self):
def isLoading(self):
""" Return True if the graph is currently being loaded. """
return self._loading
+
+ @property
+ def isSaving(self):
+ """ Return True if the graph is currently being saved. """
+ return self._saving
@Slot(str)
def load(self, filepath, setupProjectFile=True, importProject=False, publishOutputs=False):
@@ -1347,6 +1353,23 @@ def asString(self):
return str(self.toDict())
def save(self, filepath=None, setupProjectFile=True, template=False):
+ """
+ Save the current Meshroom graph as a serialized ".mg" file.
+
+ Args:
+ filepath: project filepath to save as.
+ setupProjectFile: Store the reference to the project file and setup the cache directory.
+ If false, it only saves the graph of the project file as a template.
+ template: If true, saves the current graph as a template.
+ """
+ # Update the saving flag indicating that the current graph is being saved
+ self._saving = True
+ try:
+ self._save(filepath=filepath, setupProjectFile=setupProjectFile, template=template)
+ finally:
+ self._saving = False
+
+ def _save(self, filepath=None, setupProjectFile=True, template=False):
path = filepath or self._filepath
if not path:
raise ValueError("filepath must be specified for unsaved files.")
@@ -1636,6 +1659,7 @@ def setVerbose(self, v):
edges = Property(BaseObject, edges.fget, constant=True)
filepathChanged = Signal()
filepath = Property(str, lambda self: self._filepath, notify=filepathChanged)
+ isSaving = Property(bool, isSaving.fget, constant=True)
fileReleaseVersion = Property(str, lambda self: self.header.get(Graph.IO.Keys.ReleaseVersion, "0.0"),
notify=filepathChanged)
fileDateVersion = Property(float, fileDateVersion.fget, fileDateVersion.fset, notify=filepathChanged)
diff --git a/meshroom/ui/qml/Application.qml b/meshroom/ui/qml/Application.qml
index 48884e2f33..903c9241fb 100644
--- a/meshroom/ui/qml/Application.qml
+++ b/meshroom/ui/qml/Application.qml
@@ -20,6 +20,16 @@ Page {
property alias unsavedDialog: unsavedDialog
property alias workspaceView: workspaceView
+ readonly property var scenefile: _reconstruction ? _reconstruction.graph.filepath : "";
+
+ onScenefileChanged: {
+ // Check if we're not currently saving and emit the currentProjectChanged signal
+ if (! _reconstruction.graph.isSaving) {
+ // Refresh the NodeEditor
+ nodeEditor.refresh();
+ }
+ }
+
Settings {
id: settingsUILayout
category: "UILayout"
diff --git a/meshroom/ui/qml/GraphEditor/NodeEditor.qml b/meshroom/ui/qml/GraphEditor/NodeEditor.qml
index a97400ab7a..5f222f4231 100644
--- a/meshroom/ui/qml/GraphEditor/NodeEditor.qml
+++ b/meshroom/ui/qml/GraphEditor/NodeEditor.qml
@@ -42,6 +42,14 @@ Panel {
}
}
+ function refresh() {
+ /**
+ * Refresh properties of the Node Editor.
+ */
+ // Reset tab bar's current index
+ tabBar.currentIndex = 0;
+ }
+
headerBar: RowLayout {
Label {
id: computationInfo
@@ -372,6 +380,9 @@ Panel {
property bool isComputable: root.node !== null && root.node.isComputable
+ // The indices of the tab bar which can be shown for incomputable nodes
+ readonly property var nonComputableTabIndices: [0, 4, 5];
+
Layout.fillWidth: true
width: childrenRect.width
position: TabBar.Footer
@@ -415,9 +426,11 @@ Panel {
rightPadding: leftPadding
}
- onIsComputableChanged: {
- if (!isComputable) {
- tabBar.currentIndex = 0
+ onVisibleChanged: {
+ // If we have a node selected and the node is not Computable
+ // Reset the currentIndex to 0, if the current index is not allowed for an incomputable node
+ if ((root.node && !root.node.isComputable) && (nonComputableTabIndices.indexOf(tabBar.currentIndex) === -1)) {
+ tabBar.currentIndex = 0;
}
}
}
diff --git a/meshroom/ui/qml/GraphEditor/StatViewer.qml b/meshroom/ui/qml/GraphEditor/StatViewer.qml
index 91e224681a..ed25d577ee 100644
--- a/meshroom/ui/qml/GraphEditor/StatViewer.qml
+++ b/meshroom/ui/qml/GraphEditor/StatViewer.qml
@@ -310,220 +310,208 @@ Item {
*** CPU UI ***
**************************/
- ColumnLayout {
+ Button {
+ id: toggleCpuBtn
Layout.fillWidth: true
+ text: "Toggle CPU's"
+ state: "closed"
- Button {
- id: toggleCpuBtn
- Layout.fillWidth: true
- text: "Toggle CPU's"
- state: "closed"
+ onClicked: state === "opened" ? state = "closed" : state = "opened"
- onClicked: state === "opened" ? state = "closed" : state = "opened"
+ MaterialLabel {
+ text: MaterialIcons.arrow_drop_down
+ font.pointSize: 14
+ anchors.right: parent.right
+ }
- MaterialLabel {
- text: MaterialIcons.arrow_drop_down
- font.pointSize: 14
- anchors.right: parent.right
+ states: [
+ State {
+ name: "opened"
+ PropertyChanges { target: cpuBtnContainer; visible: true }
+ PropertyChanges { target: toggleCpuBtn; down: true }
+ },
+ State {
+ name: "closed"
+ PropertyChanges { target: cpuBtnContainer; visible: false }
+ PropertyChanges { target: toggleCpuBtn; down: false }
}
+ ]
+ }
- states: [
- State {
- name: "opened"
- PropertyChanges { target: cpuBtnContainer; visible: true }
- PropertyChanges { target: toggleCpuBtn; down: true }
- },
- State {
- name: "closed"
- PropertyChanges { target: cpuBtnContainer; visible: false }
- PropertyChanges { target: toggleCpuBtn; down: false }
- }
- ]
- }
+ Item {
+ id: cpuBtnContainer
- Item {
- id: cpuBtnContainer
-
- Layout.fillWidth: true
- implicitHeight: childrenRect.height
- Layout.leftMargin: 25
-
- RowLayout {
- width: parent.width
- anchors.horizontalCenter: parent.horizontalCenter
-
- ChartViewCheckBox {
- id: allCPU
- text: "ALL"
- color: textColor
- checkState: cpuLegend.buttonGroup.checkState
- leftPadding: 0
- onClicked: {
- var _checked = checked;
- for (var i = 0; i < cpuChart.count; ++i) {
- cpuChart.series(i).visible = _checked
- }
- }
- }
+ Layout.fillWidth: true
+ implicitHeight: childrenRect.height
+ Layout.leftMargin: 25
- ChartViewLegend {
- id: cpuLegend
- Layout.fillWidth: true
- Layout.fillHeight: true
- chartView: cpuChart
- }
- }
- }
+ RowLayout {
+ width: parent.width
+ anchors.horizontalCenter: parent.horizontalCenter
- InteractiveChartView {
- id: cpuChart
-
- Layout.fillWidth: true
- Layout.preferredHeight: width / 2
- margins.top: 0
- margins.bottom: 0
- antialiasing: true
-
- legend.visible: false
- theme: ChartView.ChartThemeLight
- backgroundColor: "transparent"
- plotAreaColor: "transparent"
- titleColor: textColor
-
- visible: (root.fileVersion > 0.0) // Only visible if we have valid information
- title: "CPU: " + root.nbCores + " cores, " + root.cpuFrequency + "MHz"
-
- ValueAxis {
- id: valueCpuY
- min: 0
- max: 100
- titleText: "%"
+ ChartViewCheckBox {
+ id: allCPU
+ text: "ALL"
color: textColor
- gridLineColor: textColor
- minorGridLineColor: textColor
- shadesColor: textColor
- shadesBorderColor: textColor
- labelsColor: textColor
+ checkState: cpuLegend.buttonGroup.checkState
+ leftPadding: 0
+ onClicked: {
+ var _checked = checked;
+ for (var i = 0; i < cpuChart.count; ++i) {
+ cpuChart.series(i).visible = _checked
+ }
+ }
}
- ValueAxis {
- id: valueCpuX
- min: 0
- max: root.deltaTime * Math.max(1, root.nbReads)
- titleText: "Minutes"
- color: textColor
- gridLineColor: textColor
- minorGridLineColor: textColor
- shadesColor: textColor
- shadesBorderColor: textColor
- labelsColor: textColor
+ ChartViewLegend {
+ id: cpuLegend
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ chartView: cpuChart
}
}
}
+ InteractiveChartView {
+ id: cpuChart
+
+ Layout.fillWidth: true
+ Layout.preferredHeight: width / 2
+ margins.top: 0
+ margins.bottom: 0
+ antialiasing: true
+
+ legend.visible: false
+ theme: ChartView.ChartThemeLight
+ backgroundColor: "transparent"
+ plotAreaColor: "transparent"
+ titleColor: textColor
+
+ visible: (root.fileVersion > 0.0) // Only visible if we have valid information
+ title: "CPU: " + root.nbCores + " cores, " + root.cpuFrequency + "MHz"
+
+ ValueAxis {
+ id: valueCpuY
+ min: 0
+ max: 100
+ titleText: "%"
+ color: textColor
+ gridLineColor: textColor
+ minorGridLineColor: textColor
+ shadesColor: textColor
+ shadesBorderColor: textColor
+ labelsColor: textColor
+ }
+
+ ValueAxis {
+ id: valueCpuX
+ min: 0
+ max: root.deltaTime * Math.max(1, root.nbReads)
+ titleText: "Minutes"
+ color: textColor
+ gridLineColor: textColor
+ minorGridLineColor: textColor
+ shadesColor: textColor
+ shadesBorderColor: textColor
+ labelsColor: textColor
+ }
+ }
/**************************
*** RAM UI ***
**************************/
- ColumnLayout {
-
- InteractiveChartView {
- id: ramChart
- margins.top: 0
- margins.bottom: 0
- Layout.fillWidth: true
- Layout.preferredHeight: width / 2
- antialiasing: true
- legend.color: textColor
- legend.labelColor: textColor
- legend.visible: false
- theme: ChartView.ChartThemeLight
- backgroundColor: "transparent"
- plotAreaColor: "transparent"
- titleColor: textColor
-
- visible: (root.fileVersion > 0.0) // Only visible if we have valid information
- title: root.ramLabel + root.ramTotal + "GB"
-
- ValueAxis {
- id: valueRamY
- min: 0
- max: 100
- titleText: "%"
- color: textColor
- gridLineColor: textColor
- minorGridLineColor: textColor
- shadesColor: textColor
- shadesBorderColor: textColor
- labelsColor: textColor
- }
+ InteractiveChartView {
+ id: ramChart
+ margins.top: 0
+ margins.bottom: 0
+ Layout.fillWidth: true
+ Layout.preferredHeight: width / 2
+ antialiasing: true
+ legend.color: textColor
+ legend.labelColor: textColor
+ legend.visible: false
+ theme: ChartView.ChartThemeLight
+ backgroundColor: "transparent"
+ plotAreaColor: "transparent"
+ titleColor: textColor
+
+ visible: (root.fileVersion > 0.0) // Only visible if we have valid information
+ title: root.ramLabel + root.ramTotal + "GB"
+
+ ValueAxis {
+ id: valueRamY
+ min: 0
+ max: 100
+ titleText: "%"
+ color: textColor
+ gridLineColor: textColor
+ minorGridLineColor: textColor
+ shadesColor: textColor
+ shadesBorderColor: textColor
+ labelsColor: textColor
+ }
- ValueAxis {
- id: valueRamX
- min: 0
- max: root.deltaTime * Math.max(1, root.nbReads)
- titleText: "Minutes"
- color: textColor
- gridLineColor: textColor
- minorGridLineColor: textColor
- shadesColor: textColor
- shadesBorderColor: textColor
- labelsColor: textColor
- }
+ ValueAxis {
+ id: valueRamX
+ min: 0
+ max: root.deltaTime * Math.max(1, root.nbReads)
+ titleText: "Minutes"
+ color: textColor
+ gridLineColor: textColor
+ minorGridLineColor: textColor
+ shadesColor: textColor
+ shadesBorderColor: textColor
+ labelsColor: textColor
}
}
-
/**************************
*** GPU UI ***
**************************/
- ColumnLayout {
-
- InteractiveChartView {
- id: gpuChart
-
- Layout.fillWidth: true
- Layout.preferredHeight: width/2
- margins.top: 0
- margins.bottom: 0
- antialiasing: true
- legend.color: textColor
- legend.labelColor: textColor
- theme: ChartView.ChartThemeLight
- backgroundColor: "transparent"
- plotAreaColor: "transparent"
- titleColor: textColor
-
- visible: (root.fileVersion >= 2.0) // No GPU information was collected before stats 2.0 fileVersion
- title: (root.gpuName || root.gpuTotalMemory) ? ("GPU: " + root.gpuName + ", " + root.gpuTotalMemory + "MB") : "No GPU"
-
- ValueAxis {
- id: valueGpuY
- min: 0
- max: root.gpuMaxAxis
- titleText: "%, °C"
- color: textColor
- gridLineColor: textColor
- minorGridLineColor: textColor
- shadesColor: textColor
- shadesBorderColor: textColor
- labelsColor: textColor
- }
+ InteractiveChartView {
+ id: gpuChart
- ValueAxis {
- id: valueGpuX
- min: 0
- max: root.deltaTime * Math.max(1, root.nbReads)
- titleText: "Minutes"
- color: textColor
- gridLineColor: textColor
- minorGridLineColor: textColor
- shadesColor: textColor
- shadesBorderColor: textColor
- labelsColor: textColor
- }
+ Layout.fillWidth: true
+ Layout.preferredHeight: width/2
+ margins.top: 0
+ margins.bottom: 0
+ antialiasing: true
+ legend.color: textColor
+ legend.labelColor: textColor
+ theme: ChartView.ChartThemeLight
+ backgroundColor: "transparent"
+ plotAreaColor: "transparent"
+ titleColor: textColor
+
+ visible: (root.fileVersion >= 2.0) // No GPU information was collected before stats 2.0 fileVersion
+ title: (root.gpuName || root.gpuTotalMemory) ? ("GPU: " + root.gpuName + ", " + root.gpuTotalMemory + "MB") : "No GPU"
+
+ ValueAxis {
+ id: valueGpuY
+ min: 0
+ max: root.gpuMaxAxis
+ titleText: "%, °C"
+ color: textColor
+ gridLineColor: textColor
+ minorGridLineColor: textColor
+ shadesColor: textColor
+ shadesBorderColor: textColor
+ labelsColor: textColor
+ }
+
+ ValueAxis {
+ id: valueGpuX
+ min: 0
+ max: root.deltaTime * Math.max(1, root.nbReads)
+ titleText: "Minutes"
+ color: textColor
+ gridLineColor: textColor
+ minorGridLineColor: textColor
+ shadesColor: textColor
+ shadesBorderColor: textColor
+ labelsColor: textColor
}
}
}