From 2f0996f7bf70adf9281709c4507260837698a002 Mon Sep 17 00:00:00 2001 From: waaake Date: Mon, 23 Dec 2024 09:07:29 +0530 Subject: [PATCH 1/5] [ui] NodeEditor: Addressed the issue with retaining tabs when switching node selection Updated the event trigger to be onVisibleChanged and the condition to check whether the current selected node is an incomputable node and the current tab index does not exist for it before resetting the index to 0 --- meshroom/ui/qml/GraphEditor/NodeEditor.qml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/meshroom/ui/qml/GraphEditor/NodeEditor.qml b/meshroom/ui/qml/GraphEditor/NodeEditor.qml index a97400ab7a..9110390386 100644 --- a/meshroom/ui/qml/GraphEditor/NodeEditor.qml +++ b/meshroom/ui/qml/GraphEditor/NodeEditor.qml @@ -372,6 +372,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 +418,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; } } } From d142a745d0991b821506e1739213ac09198f2151 Mon Sep 17 00:00:00 2001 From: waaake Date: Mon, 30 Dec 2024 09:28:36 +0530 Subject: [PATCH 2/5] [fix][ui] StatViewer: Updated Layouts for Stat Viewer The sub column layouts in the parent column layouts were causing polish loop during show/resize ops --- meshroom/ui/qml/GraphEditor/StatViewer.qml | 356 ++++++++++----------- 1 file changed, 172 insertions(+), 184 deletions(-) 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 } } } From 535f2f06b367133efc86edfccf0843285d353aa6 Mon Sep 17 00:00:00 2001 From: waaake Date: Mon, 30 Dec 2024 10:25:03 +0530 Subject: [PATCH 3/5] [core] Graph: Introduced 'isSaving' flag The 'isSaving' flag is a way to identify if the project is currently being saved and serves as a way to correctly distinguish whether the current filepath change is due to a save or a new scene or a load operation --- meshroom/core/graph.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/meshroom/core/graph.py b/meshroom/core/graph.py index e63aceca1a..76615ae619 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 loaded. """ + 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) From a2f4ac560fedda4a0a015c6164ef3470964e82f1 Mon Sep 17 00:00:00 2001 From: waaake Date: Tue, 31 Dec 2024 09:43:27 +0530 Subject: [PATCH 4/5] [ui] NodeEditor: Added refresh function to update the currentIndex of the tabBar --- meshroom/ui/qml/GraphEditor/NodeEditor.qml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/meshroom/ui/qml/GraphEditor/NodeEditor.qml b/meshroom/ui/qml/GraphEditor/NodeEditor.qml index 9110390386..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 From 3f47c54595e0ab42e8fc2c7821013b010c26cb6c Mon Sep 17 00:00:00 2001 From: waaake Date: Tue, 31 Dec 2024 09:56:48 +0530 Subject: [PATCH 5/5] [ui] Application: Changing the current project refreshes the NodeEditor to reset the current Tab index --- meshroom/core/graph.py | 2 +- meshroom/ui/qml/Application.qml | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/meshroom/core/graph.py b/meshroom/core/graph.py index 76615ae619..7808f1e235 100644 --- a/meshroom/core/graph.py +++ b/meshroom/core/graph.py @@ -255,7 +255,7 @@ def isLoading(self): @property def isSaving(self): - """ Return True if the graph is currently being loaded. """ + """ Return True if the graph is currently being saved. """ return self._saving @Slot(str) 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"