From 02383c68b3284ef7505e6ecc277b41f960b6ed38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Candice=20Bent=C3=A9jac?= Date: Fri, 2 Dec 2022 15:49:51 +0100 Subject: [PATCH] [ui] Check that objects accessed by QML properties are not null before accessing them Some QML properties access exposed Python objects that may or may not be null upon their access. When these objects are accessed while null, QML issues "TypeError" warnings. These warnings have no functional impact as QML correctly handles trying to access null objects, but can spam the logs. This commit aims at fixing all these warnings by checking that the Python objects are not null before being accessed. --- .../qml/GraphEditor/CompatibilityManager.qml | 7 +- meshroom/ui/qml/GraphEditor/GraphEditor.qml | 4 +- .../ui/qml/GraphEditor/NodeDocumentation.qml | 2 +- meshroom/ui/qml/GraphEditor/TaskManager.qml | 4 +- .../ui/qml/ImageGallery/ImageDelegate.qml | 2 +- meshroom/ui/qml/ImageGallery/ImageGallery.qml | 32 +++++---- meshroom/ui/qml/LiveSfmView.qml | 10 +-- meshroom/ui/qml/Viewer/Viewer2D.qml | 42 ++++++------ meshroom/ui/qml/Viewer3D/AlembicLoader.qml | 2 +- meshroom/ui/qml/Viewer3D/Inspector3D.qml | 6 +- meshroom/ui/qml/Viewer3D/Viewer3D.qml | 2 +- meshroom/ui/qml/WorkspaceView.qml | 12 ++-- meshroom/ui/qml/main.qml | 68 ++++++++++--------- 13 files changed, 103 insertions(+), 90 deletions(-) diff --git a/meshroom/ui/qml/GraphEditor/CompatibilityManager.qml b/meshroom/ui/qml/GraphEditor/CompatibilityManager.qml index d9cad1ee..d2ea318d 100644 --- a/meshroom/ui/qml/GraphEditor/CompatibilityManager.qml +++ b/meshroom/ui/qml/GraphEditor/CompatibilityManager.qml @@ -14,7 +14,7 @@ MessageDialog { // the UIGraph instance property var uigraph // alias to underlying compatibilityNodes model - readonly property var nodesModel: uigraph.graph.compatibilityNodes + readonly property var nodesModel: uigraph ? uigraph.graph.compatibilityNodes : undefined // the total number of compatibility issues readonly property int issueCount: (nodesModel != undefined) ? nodesModel.count : 0 // the number of CompatibilityNodes that can be upgraded @@ -47,7 +47,10 @@ MessageDialog { title: "Compatibility issues detected" text: "This project contains " + issueCount + " node(s) incompatible with the current version of Meshroom." - detailedText: "Project was created with Meshroom " + uigraph.graph.fileReleaseVersion + "." + detailedText: { + let releaseVersion = uigraph ? uigraph.graph.fileReleaseVersion : "0.0" + return "Project was created with Meshroom " + releaseVersion + "." + } helperText: upgradableCount ? upgradableCount + " node(s) can be upgraded but this might invalidate already computed data.\n" diff --git a/meshroom/ui/qml/GraphEditor/GraphEditor.qml b/meshroom/ui/qml/GraphEditor/GraphEditor.qml index 36ccc4a4..bc156a8a 100755 --- a/meshroom/ui/qml/GraphEditor/GraphEditor.qml +++ b/meshroom/ui/qml/GraphEditor/GraphEditor.qml @@ -418,7 +418,7 @@ Item { MenuItem { text: "Submit" enabled: nodeMenu.canComputeNode && nodeMenu.canSubmitOrCompute > 1 - visible: uigraph.canSubmit + visible: uigraph ? uigraph.canSubmit : false height: visible ? implicitHeight : 0 onTriggered: submitRequest(nodeMenu.currentNode) } @@ -736,7 +736,7 @@ Item { flat: true model: ['Minimum', 'Maximum'] implicitWidth: 80 - currentIndex: uigraph.layout.depthMode + currentIndex: uigraph ? uigraph.layout.depthMode : -1 onActivated: { uigraph.layout.depthMode = currentIndex } diff --git a/meshroom/ui/qml/GraphEditor/NodeDocumentation.qml b/meshroom/ui/qml/GraphEditor/NodeDocumentation.qml index 337f3e37..3c63d2ef 100644 --- a/meshroom/ui/qml/GraphEditor/NodeDocumentation.qml +++ b/meshroom/ui/qml/GraphEditor/NodeDocumentation.qml @@ -31,7 +31,7 @@ FocusScope { selectByMouse: true selectionColor: activePalette.highlight color: activePalette.text - text: node.documentation + text: node ? node.documentation : "" wrapMode: TextEdit.Wrap } } diff --git a/meshroom/ui/qml/GraphEditor/TaskManager.qml b/meshroom/ui/qml/GraphEditor/TaskManager.qml index 52fa209d..aa48e43b 100644 --- a/meshroom/ui/qml/GraphEditor/TaskManager.qml +++ b/meshroom/ui/qml/GraphEditor/TaskManager.qml @@ -39,7 +39,7 @@ Item { TextMetrics { id: nbMetrics - text: root.taskManager.nodes.count + text: root.taskManager ? root.taskManager.nodes.count : "0" } TextMetrics { @@ -67,7 +67,7 @@ Item { anchors.fill: parent ScrollBar.vertical: ScrollBar {} - model: parent.taskManager.nodes + model: parent.taskManager ? parent.taskManager.nodes : null spacing: 3 headerPositioning: ListView.OverlayHeader diff --git a/meshroom/ui/qml/ImageGallery/ImageDelegate.qml b/meshroom/ui/qml/ImageGallery/ImageDelegate.qml index bc5beb11..484c172f 100644 --- a/meshroom/ui/qml/ImageGallery/ImageDelegate.qml +++ b/meshroom/ui/qml/ImageGallery/ImageDelegate.qml @@ -58,7 +58,7 @@ Item { } MenuItem { text: "Define As Center Image" - property var activeNode: _reconstruction.activeNodes.get("SfMTransform").node + property var activeNode: _reconstruction ? _reconstruction.activeNodes.get("SfMTransform").node : null enabled: !root.readOnly && _viewpoint.viewId != -1 && _reconstruction && activeNode onClicked: activeNode.attribute("transformation").value = _viewpoint.viewId.toString() } diff --git a/meshroom/ui/qml/ImageGallery/ImageGallery.qml b/meshroom/ui/qml/ImageGallery/ImageGallery.qml index 52d08802..3f2a58c2 100644 --- a/meshroom/ui/qml/ImageGallery/ImageGallery.qml +++ b/meshroom/ui/qml/ImageGallery/ImageGallery.qml @@ -35,7 +35,7 @@ Panel { QtObject { id: m - property variant currentCameraInit: _reconstruction.tempCameraInit ? _reconstruction.tempCameraInit : root.cameraInit + property variant currentCameraInit: _reconstruction && _reconstruction.tempCameraInit ? _reconstruction.tempCameraInit : root.cameraInit property variant viewpoints: currentCameraInit ? currentCameraInit.attribute('viewpoints').value : undefined property variant intrinsics: currentCameraInit ? currentCameraInit.attribute('intrinsics').value : undefined property bool readOnly: root.readOnly || displayHDR.checked @@ -144,7 +144,7 @@ Panel { SensorDBDialog { id: sensorDBDialog sensorDatabase: cameraInit ? Filepath.stringToUrl(cameraInit.attribute("sensorDatabase").value) : "" - readOnly: _reconstruction.computing + readOnly: _reconstruction ? _reconstruction.computing : false onUpdateIntrinsicsRequest: _reconstruction.rebuildIntrinsics(cameraInit) } @@ -273,11 +273,11 @@ Panel { spacing: 2 property bool valid: Qt.isQtObject(object) // object can be evaluated to null at some point during creation/deletion - property bool inViews: valid && _reconstruction.sfmReport && _reconstruction.isInViews(object) + property bool inViews: valid && _reconstruction && _reconstruction.sfmReport && _reconstruction.isInViews(object) // Camera Initialization indicator IntrinsicsIndicator { - intrinsic: parent.valid ? _reconstruction.getIntrinsic(object) : null + intrinsic: parent.valid && _reconstruction ? _reconstruction.getIntrinsic(object) : null metadata: imageDelegate.metadata } @@ -540,7 +540,7 @@ Panel { RowLayout { Layout.fillHeight: false - visible: root.cameraInits.count > 1 + visible: root.cameraInits ? root.cameraInits.count > 1 : false Layout.alignment: Qt.AlignHCenter spacing: 2 @@ -560,8 +560,10 @@ Panel { // display of group indices (real indices still are from // 0 to cameraInits.count - 1) var l = []; - for (var i = 1; i <= root.cameraInits.count; i++) { - l.push(i); + if (root.cameraInits) { + for (var i = 1; i <= root.cameraInits.count; i++) { + l.push(i); + } } return l; } @@ -569,13 +571,13 @@ Panel { currentIndex: root.cameraInitIndex onActivated: root.changeCurrentIndex(currentIndex) } - Label { text: "/ " + (root.cameraInits.count) } + Label { text: "/ " + (root.cameraInits ? root.cameraInits.count : "Unknown") } ToolButton { text: MaterialIcons.navigate_next font.family: MaterialIcons.fontFamily ToolTip.text: "Next Group (Alt+Right)" ToolTip.visible: hovered - enabled: nodesCB.currentIndex < root.cameraInits.count - 1 + enabled: root.cameraInits ? nodesCB.currentIndex < root.cameraInits.count - 1 : false onClicked: nodesCB.incrementCurrentIndex() } } @@ -617,10 +619,10 @@ Panel { Layout.minimumWidth: childrenRect.width ToolTip.text: label + " Estimated Cameras" iconText: MaterialIcons.videocam - label: _reconstruction.nbCameras ? _reconstruction.nbCameras.toString() : "-" + label: _reconstruction && _reconstruction.nbCameras ? _reconstruction.nbCameras.toString() : "-" padding: 3 - enabled: _reconstruction.cameraInit && _reconstruction.nbCameras + enabled: _reconstruction ? _reconstruction.cameraInit && _reconstruction.nbCameras : false checkable: true checked: false @@ -646,10 +648,10 @@ Panel { Layout.minimumWidth: childrenRect.width ToolTip.text: label + " Non Estimated Cameras" iconText: MaterialIcons.videocam_off - label: _reconstruction.nbCameras ? ((m.viewpoints ? m.viewpoints.count : 0) - _reconstruction.nbCameras.toString()).toString() : "-" + label: _reconstruction && _reconstruction.nbCameras ? ((m.viewpoints ? m.viewpoints.count : 0) - _reconstruction.nbCameras.toString()).toString() : "-" padding: 3 - enabled: _reconstruction.cameraInit && _reconstruction.nbCameras + enabled: _reconstruction ? _reconstruction.cameraInit && _reconstruction.nbCameras : false checkable: true checked: false @@ -704,7 +706,7 @@ Panel { MaterialToolLabelButton { id: displayHDR Layout.minimumWidth: childrenRect.width - property var activeNode: _reconstruction.activeNodes.get("LdrToHdrMerge").node + property var activeNode: _reconstruction ? _reconstruction.activeNodes.get("LdrToHdrMerge").node : null ToolTip.text: "Visualize HDR images: " + (activeNode ? activeNode.label : "No Node") iconText: MaterialIcons.filter label: activeNode ? activeNode.attribute("nbBrackets").value : "" @@ -747,7 +749,7 @@ Panel { id: imageProcessing Layout.minimumWidth: childrenRect.width - property var activeNode: _reconstruction.activeNodes.get("ImageProcessing").node + property var activeNode: _reconstruction ? _reconstruction.activeNodes.get("ImageProcessing").node : null font.pointSize: 15 padding: 0 ToolTip.text: "Preprocessed Images: " + (activeNode ? activeNode.label : "No Node") diff --git a/meshroom/ui/qml/LiveSfmView.qml b/meshroom/ui/qml/LiveSfmView.qml index b8190de9..8062859a 100644 --- a/meshroom/ui/qml/LiveSfmView.qml +++ b/meshroom/ui/qml/LiveSfmView.qml @@ -13,7 +13,7 @@ Panel { id: root property variant reconstruction - readonly property variant liveSfmManager: reconstruction.liveSfmManager + readonly property variant liveSfmManager: reconstruction ? reconstruction.liveSfmManager : null title: "Live Reconstruction" icon: Label { @@ -41,7 +41,7 @@ Panel { width: parent.width GroupBox { Layout.fillWidth: true - enabled: !liveSfmManager.running + enabled: liveSfmManager ? !liveSfmManager.running : false GridLayout { width: parent.width @@ -59,7 +59,7 @@ Panel { id: folderPath Layout.fillWidth: true selectByMouse: true - text: liveSfmManager.folder + text: liveSfmManager ? liveSfmManager.folder : "" placeholderText: "Select a Folder" } ToolButton { @@ -90,8 +90,8 @@ Panel { Button { Layout.alignment: Qt.AlignCenter text: checked ? "Stop" : "Start" - enabled: liveSfmManager.running || folderPath.text.trim() != '' - checked: liveSfmManager.running + enabled: liveSfmManager ? liveSfmManager.running || folderPath.text.trim() != '' : false + checked: liveSfmManager ? liveSfmManager.running : false onClicked: { if(!liveSfmManager.running) liveSfmManager.start(folderPath.text, minImg_SB.value) diff --git a/meshroom/ui/qml/Viewer/Viewer2D.qml b/meshroom/ui/qml/Viewer/Viewer2D.qml index c2db199b..12a67946 100644 --- a/meshroom/ui/qml/Viewer/Viewer2D.qml +++ b/meshroom/ui/qml/Viewer/Viewer2D.qml @@ -24,7 +24,7 @@ FocusScope { property alias useLensDistortionViewer: displayLensDistortionViewer.checked property alias usePanoramaViewer: displayPanoramaViewer.checked - property var activeNodeFisheye: _reconstruction.activeNodes.get("PanoramaInit").node + property var activeNodeFisheye: _reconstruction ? _reconstruction.activeNodes.get("PanoramaInit").node : null property bool cropFisheye : activeNodeFisheye ? activeNodeFisheye.attribute("useFisheye").value : false property bool enable8bitViewer: MeshroomApp.default8bitViewerEnabled @@ -202,15 +202,15 @@ FocusScope { if (useExternal) { return sourceExternal; } - if (!displayedNode || outputAttribute.name == "gallery") { + if (_reconstruction && (!displayedNode || outputAttribute.name == "gallery")) { return getViewpointPath(_reconstruction.selectedViewId); } - return getFileAttributePath(displayedNode, outputAttribute.name, _reconstruction.selectedViewId); + return getFileAttributePath(displayedNode, outputAttribute.name, _reconstruction ? _reconstruction.selectedViewId : -1); } function getMetadata() { // entry point for getting the image metadata - if (useExternal) { + if (useExternal || !_reconstruction) { return {}; } else { return getViewpointMetadata(_reconstruction.selectedViewId); @@ -220,6 +220,8 @@ FocusScope { function getFileAttributePath(node, attrName, viewId) { // get output attribute with matching name // and parse its value to get the image filepath + if (!node) + return ""; for (var i = 0; i < node.attributes.count; i++) { var attr = node.attributes.at(i); if (attr.name == attrName) { @@ -429,7 +431,7 @@ FocusScope { 'sfmRequired': Qt.binding(function(){ return displayLensDistortionViewer.checked ? true : false;}), 'surface.msfmData': Qt.binding(function() { return (msfmDataLoader.status === Loader.Ready && msfmDataLoader.item != null && msfmDataLoader.item.status === 2) ? msfmDataLoader.item : null; }), 'canBeHovered': false, - 'idView': Qt.binding(function() { return _reconstruction.selectedViewId; }), + 'idView': Qt.binding(function() { return (_reconstruction ? _reconstruction.selectedViewId : -1); }), 'cropFisheye': false }) } else { @@ -521,7 +523,7 @@ FocusScope { Loader { id: featuresViewerLoader active: displayFeatures.checked - property var activeNode: _reconstruction.activeNodes.get("FeatureExtraction").node + property var activeNode: _reconstruction ? _reconstruction.activeNodes.get("FeatureExtraction").node : null // handle rotation/position based on available metadata rotation: { @@ -554,7 +556,7 @@ FocusScope { // note: use a Loader to evaluate if a PanoramaInit node exist and displayFisheyeCircle checked at runtime Loader { anchors.centerIn: parent - property var activeNode: _reconstruction.activeNodes.get("PanoramaInit").node + property var activeNode: _reconstruction ? _reconstruction.activeNodes.get("PanoramaInit").node : null active: (displayFisheyeCircleLoader.checked && activeNode) // handle rotation/position based on available metadata @@ -602,7 +604,7 @@ FocusScope { Loader { id: colorCheckerViewerLoader anchors.centerIn: parent - property var activeNode: _reconstruction.activeNodes.get("ColorCheckerDetection").node + property var activeNode: _reconstruction ? _reconstruction.activeNodes.get("ColorCheckerDetection").node : null active: (displayColorCheckerViewerLoader.checked && activeNode) @@ -706,7 +708,7 @@ FocusScope { id: mfeaturesLoader property bool isUsed: displayFeatures.checked - property var activeNode: root.aliceVisionPluginAvailable ? _reconstruction.activeNodes.get("FeatureExtraction").node : null + property var activeNode: root.aliceVisionPluginAvailable && _reconstruction ? _reconstruction.activeNodes.get("FeatureExtraction").node : null property bool isComputed: activeNode && activeNode.isComputed active: false @@ -749,7 +751,7 @@ FocusScope { } // For lens distortion viewer: use all nodes creating a sfmData file var nodeType = displayLensDistortionViewer.checked ? 'sfmData' : 'sfm' - var sfmNode = _reconstruction.activeNodes.get(nodeType).node + var sfmNode = _reconstruction ? _reconstruction.activeNodes.get(nodeType).node : null if(sfmNode === null){ return null } @@ -827,7 +829,7 @@ FocusScope { id: mtracksLoader property bool isUsed: displayFeatures.checked || displaySfmStatsView.checked || displaySfmDataGlobalStats.checked || displayPanoramaViewer.checked - property var activeNode: root.aliceVisionPluginAvailable ? _reconstruction.activeNodes.get('FeatureMatching').node : null + property var activeNode: root.aliceVisionPluginAvailable && _reconstruction ? _reconstruction.activeNodes.get('FeatureMatching').node : null property bool isComputed: activeNode && activeNode.isComputed active: false @@ -921,7 +923,7 @@ FocusScope { id: ldrHdrCalibrationGraph anchors.fill: parent - property var activeNode: _reconstruction.activeNodes.get('LdrToHdrCalibration').node + property var activeNode: _reconstruction ? _reconstruction.activeNodes.get('LdrToHdrCalibration').node : null property var isEnabled: displayLdrHdrCalibrationGraph.checked && activeNode && activeNode.isComputed // active: isEnabled // Setting "active" from true to false creates a crash on linux with Qt 5.14.2. @@ -995,7 +997,7 @@ FocusScope { } MaterialToolButton { id: displayLensDistortionViewer - property var activeNode: root.aliceVisionPluginAvailable ? _reconstruction.activeNodes.get('sfmData').node : null + property var activeNode: root.aliceVisionPluginAvailable && _reconstruction ? _reconstruction.activeNodes.get('sfmData').node : null property bool isComputed: { if(!activeNode) return false; @@ -1031,7 +1033,7 @@ FocusScope { } MaterialToolButton { id: displayPanoramaViewer - property var activeNode: root.aliceVisionPluginAvailable ? _reconstruction.activeNodes.get('SfMTransform').node : null + property var activeNode: root.aliceVisionPluginAvailable && _reconstruction ? _reconstruction.activeNodes.get('SfMTransform').node : null property bool isComputed: { if(!activeNode) return false; @@ -1083,7 +1085,7 @@ FocusScope { } MaterialToolButton { id: displayFisheyeCircleLoader - property var activeNode: _reconstruction.activeNodes.get('PanoramaInit').node + property var activeNode: _reconstruction ? _reconstruction.activeNodes.get('PanoramaInit').node : null ToolTip.text: "Display Fisheye Circle: " + (activeNode ? activeNode.label : "No Node") text: MaterialIcons.vignette // text: MaterialIcons.panorama_fish_eye @@ -1096,7 +1098,7 @@ FocusScope { } MaterialToolButton { id: displayColorCheckerViewerLoader - property var activeNode: _reconstruction.activeNodes.get('ColorCheckerDetection').node + property var activeNode: _reconstruction ? _reconstruction.activeNodes.get('ColorCheckerDetection').node : null ToolTip.text: "Display Color Checker: " + (activeNode ? activeNode.label : "No Node") text: MaterialIcons.view_comfy //view_module grid_on gradient view_comfy border_all font.pointSize: 11 @@ -1121,7 +1123,7 @@ FocusScope { MaterialToolButton { id: displayLdrHdrCalibrationGraph - property var activeNode: _reconstruction.activeNodes.get("LdrToHdrCalibration").node + property var activeNode: _reconstruction ? _reconstruction.activeNodes.get("LdrToHdrCalibration").node : null property bool isComputed: activeNode && activeNode.isComputed ToolTip.text: "Display Camera Response Function: " + (activeNode ? activeNode.label : "No Node") text: MaterialIcons.timeline @@ -1166,7 +1168,7 @@ FocusScope { } MaterialToolButton { - property var activeNode: root.oiioPluginAvailable ? _reconstruction.activeNodes.get('allDepthMap').node : null + property var activeNode: root.oiioPluginAvailable && _reconstruction ? _reconstruction.activeNodes.get('allDepthMap').node : null enabled: activeNode ToolTip.text: "View Depth Map in 3D (" + (activeNode ? activeNode.label : "No DepthMap Node Selected") + ")" text: MaterialIcons.input @@ -1180,7 +1182,7 @@ FocusScope { MaterialToolButton { id: displaySfmStatsView - property var activeNode: root.aliceVisionPluginAvailable ? _reconstruction.activeNodes.get('sfm').node : null + property var activeNode: root.aliceVisionPluginAvailable && _reconstruction ? _reconstruction.activeNodes.get('sfm').node : null font.family: MaterialIcons.fontFamily text: MaterialIcons.assessment @@ -1205,7 +1207,7 @@ FocusScope { MaterialToolButton { id: displaySfmDataGlobalStats - property var activeNode: root.aliceVisionPluginAvailable ? _reconstruction.activeNodes.get('sfm').node : null + property var activeNode: root.aliceVisionPluginAvailable && _reconstruction ? _reconstruction.activeNodes.get('sfm').node : null font.family: MaterialIcons.fontFamily text: MaterialIcons.language diff --git a/meshroom/ui/qml/Viewer3D/AlembicLoader.qml b/meshroom/ui/qml/Viewer3D/AlembicLoader.qml index 9b8bbf5f..0dc56462 100644 --- a/meshroom/ui/qml/Viewer3D/AlembicLoader.qml +++ b/meshroom/ui/qml/Viewer3D/AlembicLoader.qml @@ -64,7 +64,7 @@ AlembicEntity { */ PhongMaterial{ id: mat - ambient: viewId === _reconstruction.selectedViewId ? activePalette.highlight : customColor // "#CCC" + ambient: _reconstruction && viewId === _reconstruction.selectedViewId ? activePalette.highlight : customColor // "#CCC" diffuse: cameraPicker.containsMouse ? Qt.lighter(activePalette.highlight, 1.2) : ambient }, ObjectPicker { diff --git a/meshroom/ui/qml/Viewer3D/Inspector3D.qml b/meshroom/ui/qml/Viewer3D/Inspector3D.qml index 7e4ac472..5f219c9e 100644 --- a/meshroom/ui/qml/Viewer3D/Inspector3D.qml +++ b/meshroom/ui/qml/Viewer3D/Inspector3D.qml @@ -112,7 +112,7 @@ FloatingPane { // Synchronization MaterialToolButton { id: syncViewpointCamera - enabled: _reconstruction.sfmReport + enabled: _reconstruction ? _reconstruction.sfmReport : false text: MaterialIcons.linked_camera ToolTip.text: "Sync with Image Selection" checked: enabled && Viewer3DSettings.syncViewpointCamera @@ -200,8 +200,8 @@ FloatingPane { // add mediaLibrary.count in the binding to ensure 'entity' // is re-evaluated when mediaLibrary delegates are modified property bool loading: model.status === SceneLoader.Loading - property bool hovered: model.attribute ? uigraph.hoveredNode === model.attribute.node : containsMouse - property bool isSelectedNode: model.attribute ? uigraph.selectedNode === model.attribute.node : false + property bool hovered: model.attribute ? (uigraph ? uigraph.hoveredNode === model.attribute.node : false) : containsMouse + property bool isSelectedNode: model.attribute ? (uigraph ? uigraph.selectedNode === model.attribute.node : false) : false onIsSelectedNodeChanged: updateCurrentIndex() diff --git a/meshroom/ui/qml/Viewer3D/Viewer3D.qml b/meshroom/ui/qml/Viewer3D/Viewer3D.qml index 5e0e390f..89b46274 100644 --- a/meshroom/ui/qml/Viewer3D/Viewer3D.qml +++ b/meshroom/ui/qml/Viewer3D/Viewer3D.qml @@ -26,7 +26,7 @@ FocusScope { readonly property vector3d defaultCamUpVector: Qt.vector3d(0.0, 1.0, 0.0) readonly property vector3d defaultCamViewCenter: Qt.vector3d(0.0, 0.0, 0.0) - readonly property var viewpoint: _reconstruction.selectedViewpoint + readonly property var viewpoint: _reconstruction ? _reconstruction.selectedViewpoint : null readonly property bool doSyncViewpointCamera: Viewer3DSettings.syncViewpointCamera && (viewpoint && viewpoint.isReconstructed) // functions diff --git a/meshroom/ui/qml/WorkspaceView.qml b/meshroom/ui/qml/WorkspaceView.qml index 2c4d6ae2..9379da77 100644 --- a/meshroom/ui/qml/WorkspaceView.qml +++ b/meshroom/ui/qml/WorkspaceView.qml @@ -20,7 +20,7 @@ Item { id: root property variant reconstruction: _reconstruction - readonly property variant cameraInits: _reconstruction.cameraInits + readonly property variant cameraInits: _reconstruction ? _reconstruction.cameraInits : null property bool readOnly: false property alias panel3dViewer: panel3dViewerLoader.item readonly property Viewer2D viewer2D: viewer2D @@ -50,7 +50,7 @@ Item { // Load reconstruction's current SfM file function viewSfM() { - var activeNode = _reconstruction.activeNodes.get('sfm').node; + var activeNode = _reconstruction.activeNodes ? _reconstruction.activeNodes.get('sfm').node : null; if(!activeNode) return; if(panel3dViewerLoader.active) { @@ -75,9 +75,9 @@ Item { Layout.fillHeight: true readOnly: root.readOnly cameraInits: root.cameraInits - cameraInit: reconstruction.cameraInit - tempCameraInit: reconstruction.tempCameraInit - cameraInitIndex: reconstruction.cameraInitIndex + cameraInit: reconstruction ? reconstruction.cameraInit : null + tempCameraInit: reconstruction ? reconstruction.tempCameraInit : null + cameraInitIndex: reconstruction ? reconstruction.cameraInitIndex : -1 onRemoveImageRequest: reconstruction.removeAttribute(attribute) onFilesDropped: reconstruction.handleFilesDrop(drop, augmentSfm ? null : cameraInit) } @@ -208,7 +208,7 @@ Item { // Load reconstructed model Button { - readonly property var outputAttribute: _reconstruction.texturing ? _reconstruction.texturing.attribute("outputMesh") : null + readonly property var outputAttribute: _reconstruction && _reconstruction.texturing ? _reconstruction.texturing.attribute("outputMesh") : null readonly property bool outputReady: outputAttribute && _reconstruction.texturing.globalStatus === "SUCCESS" readonly property int outputMediaIndex: c_viewer3D.library.find(outputAttribute) diff --git a/meshroom/ui/qml/main.qml b/meshroom/ui/qml/main.qml index a0a4071d..0c9f26d7 100755 --- a/meshroom/ui/qml/main.qml +++ b/meshroom/ui/qml/main.qml @@ -24,8 +24,8 @@ ApplicationWindow { visible: true title: { - var t = (_reconstruction.graph && _reconstruction.graph.filepath) ? _reconstruction.graph.filepath : "Untitled" - if(!_reconstruction.undoStack.clean) + var t = (_reconstruction && _reconstruction.graph && _reconstruction.graph.filepath) ? _reconstruction.graph.filepath : "Untitled" + if (_reconstruction && !_reconstruction.undoStack.clean) t += "*" t += " - " + Qt.application.name + " " + Qt.application.version return t @@ -73,12 +73,12 @@ ApplicationWindow { property var _callback: undefined - title: Filepath.basename(_reconstruction.graph.filepath) || "Unsaved Project" + title: (_reconstruction ? Filepath.basename(_reconstruction.graph.filepath) : "") || "Unsaved Project" preset: "Info" canCopy: false - text: _reconstruction.graph.filepath ? "Current project has unsaved modifications." + text: _reconstruction && _reconstruction.graph.filepath ? "Current project has unsaved modifications." : "Current project has not been saved." - helperText: _reconstruction.graph.filepath ? "Would you like to save those changes?" + helperText: _reconstruction && _reconstruction.graph.filepath ? "Would you like to save those changes?" : "Would you like to save this project?" standardButtons: Dialog.Save | Dialog.Cancel | Dialog.Discard @@ -165,14 +165,18 @@ ApplicationWindow { property bool warnIfUnsaved: true // evaluate if global reconstruction computation can be started - property bool canStartComputation: _reconstruction.viewpoints.count >= 2 // at least 2 images - && !_reconstruction.computing // computation is not started - && _reconstruction.graph.canComputeLeaves // graph has no uncomputable nodes + property bool canStartComputation: _reconstruction ? + _reconstruction.viewpoints.count >= 2 // at least 2 images + && !_reconstruction.computing // computation is not started + && _reconstruction.graph.canComputeLeaves : // graph has no uncomputable nodes + false // evaluate if graph computation can be submitted externally - property bool canSubmit: _reconstruction.canSubmit // current setup allows to compute externally - && canStartComputation // can be computed - && _reconstruction.graph.filepath // graph is saved on disk + property bool canSubmit: _reconstruction ? + _reconstruction.canSubmit // current setup allows to compute externally + && canStartComputation // can be computed + && _reconstruction.graph.filepath : // graph is saved on disk + false function compute(node, force) { if(!force && warnIfUnsaved && !_reconstruction.graph.filepath) @@ -276,7 +280,7 @@ ApplicationWindow { preset: "Warning" title: "Unsaved Project" text: "Data will be computed in the default cache folder if project remains unsaved." - detailedText: "Default cache folder: " + _reconstruction.graph.cacheDir + detailedText: "Default cache folder: " + (_reconstruction ? _reconstruction.graph.cacheDir : "unknown") helperText: "Save project first?" standardButtons: Dialog.Discard | Dialog.Cancel | Dialog.Save @@ -377,7 +381,7 @@ ApplicationWindow { // is busy building intrinsics while importing images id: buildingIntrinsicsDialog modal: true - visible: _reconstruction.buildingIntrinsics + visible: _reconstruction ? _reconstruction.buildingIntrinsics : false closePolicy: Popup.NoAutoClose title: "Initializing Cameras" icon.text: MaterialIcons.camera @@ -404,19 +408,19 @@ ApplicationWindow { Action { id: undoAction - property string tooltip: 'Undo "' +_reconstruction.undoStack.undoText +'"' + property string tooltip: 'Undo "' + (_reconstruction ? _reconstruction.undoStack.undoText : "Unknown") + '"' text: "Undo" shortcut: "Ctrl+Z" - enabled: _reconstruction.undoStack.canUndo && _reconstruction.undoStack.isUndoableIndex + enabled: _reconstruction ? _reconstruction.undoStack.canUndo && _reconstruction.undoStack.isUndoableIndex : false onTriggered: _reconstruction.undoStack.undo() } Action { id: redoAction - property string tooltip: 'Redo "' +_reconstruction.undoStack.redoText +'"' + property string tooltip: 'Redo "' + (_reconstruction ? _reconstruction.undoStack.redoText : "Unknown") + '"' text: "Redo" shortcut: "Ctrl+Shift+Z" - enabled: _reconstruction.undoStack.canRedo && !_reconstruction.undoStack.lockedRedo + enabled: _reconstruction ? _reconstruction.undoStack.canRedo && !_reconstruction.undoStack.lockedRedo : false onTriggered: _reconstruction.undoStack.redo() } Action { @@ -424,16 +428,18 @@ ApplicationWindow { property string tooltip: { var s = "Copy selected node" - s += (_reconstruction.selectedNodes.count > 1 ? "s (" : " (") + getSelectedNodesName() + s += (_reconstruction && _reconstruction.selectedNodes.count > 1 ? "s (" : " (") + getSelectedNodesName() s += ") to the clipboard" return s } - text: "Copy Node" + (_reconstruction.selectedNodes.count > 1 ? "s " : " ") - enabled: _reconstruction.selectedNodes.count > 0 + text: "Copy Node" + (_reconstruction && _reconstruction.selectedNodes.count > 1 ? "s " : " ") + enabled: _reconstruction ? _reconstruction.selectedNodes.count > 0 : false onTriggered: graphEditor.copyNodes() function getSelectedNodesName() { + if (!_reconstruction) + return "" var nodesName = "" for (var i = 0; i < _reconstruction.selectedNodes.count; i++) { @@ -612,7 +618,7 @@ ApplicationWindow { id: saveAction text: "Save" shortcut: "Ctrl+S" - enabled: (_reconstruction.graph && !_reconstruction.graph.filepath) || !_reconstruction.undoStack.clean + enabled: _reconstruction ? (_reconstruction.graph && !_reconstruction.graph.filepath) || !_reconstruction.undoStack.clean : false onTriggered: { if(_reconstruction.graph.filepath) { _reconstruction.save() @@ -742,7 +748,7 @@ ApplicationWindow { TextField { readOnly: true selectByMouse: true - text: _reconstruction.graph.cacheDir + text: _reconstruction ? _reconstruction.graph.cacheDir : "Unknown" color: Qt.darker(palette.text, 1.2) background: Item {} } @@ -809,12 +815,12 @@ ApplicationWindow { } Button { text: "Stop" - enabled: _reconstruction.computingLocally + enabled: _reconstruction ? _reconstruction.computingLocally : false onClicked: _reconstruction.stopExecution() } Item { width: 20; height: 1 } Button { - visible: _reconstruction.canSubmit + visible: _reconstruction ? _reconstruction.canSubmit : false text: "Submit" onClicked: computeManager.submit(null) } @@ -839,7 +845,7 @@ ApplicationWindow { id: chunksListView Layout.fillWidth: true height: 6 - model: _reconstruction.sortedDFSChunks + model: _reconstruction ? _reconstruction.sortedDFSChunks : null } WorkspaceView { @@ -848,7 +854,7 @@ ApplicationWindow { Layout.fillHeight: true Layout.minimumHeight: 50 reconstruction: _reconstruction - readOnly: _reconstruction.computing + readOnly: _reconstruction ? _reconstruction.computing : false function viewNode(node, mouse) { // 2D viewer @@ -899,7 +905,7 @@ ApplicationWindow { updatingStatus = false } property bool updatingStatus: false - enabled: !updatingStatus && !_reconstruction.computingLocally + enabled: !updatingStatus && (_reconstruction ? !_reconstruction.computingLocally : false) } MaterialToolButton { text: MaterialIcons.more_vert @@ -914,7 +920,7 @@ ApplicationWindow { x: -width + parent.width MenuItem { text: "Clear Pending Status" - enabled: !_reconstruction.computingLocally + enabled: _reconstruction ? !_reconstruction.computingLocally : false onTriggered: _reconstruction.graph.clearSubmittedNodes() } MenuItem { @@ -948,7 +954,7 @@ ApplicationWindow { visible: graphEditorPanel.currentTab === 1 uigraph: _reconstruction - taskManager: _reconstruction.taskManager + taskManager: _reconstruction ? _reconstruction.taskManager : null anchors.fill: parent } @@ -958,8 +964,8 @@ ApplicationWindow { NodeEditor { id: nodeEditor width: Math.round(parent.width * 0.3) - node: _reconstruction.selectedNode - property bool computing: _reconstruction.computing + node: _reconstruction ? _reconstruction.selectedNode : null + property bool computing: _reconstruction ? _reconstruction.computing : false // Make NodeEditor readOnly when computing readOnly: node ? node.locked : false