diff --git a/meshroom/ui/qml/GraphEditor/CompatibilityManager.qml b/meshroom/ui/qml/GraphEditor/CompatibilityManager.qml index d9cad1eed9..d2ea318dbf 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 3262c62258..ab352a93c0 100755 --- a/meshroom/ui/qml/GraphEditor/GraphEditor.qml +++ b/meshroom/ui/qml/GraphEditor/GraphEditor.qml @@ -441,7 +441,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) } @@ -759,7 +759,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 337f3e3711..3c63d2eff5 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 52fa209d59..aa48e43b24 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 bc5beb116b..484c172f6b 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 52d088022f..3f2a58c2b9 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 b8190de99a..8062859a1b 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 c2db199bf6..12a6794659 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 9b8bbf5f33..0dc564622c 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 7e4ac47231..5f219c9e56 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 5e0e390fb4..89b46274cd 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 2c4d6ae273..9379da77e9 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 a0a4071d8c..0c9f26d7e4 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