diff --git a/meshroom/ui/qml/GraphEditor/GraphEditor.qml b/meshroom/ui/qml/GraphEditor/GraphEditor.qml index d08b7d51..db947fcc 100755 --- a/meshroom/ui/qml/GraphEditor/GraphEditor.qml +++ b/meshroom/ui/qml/GraphEditor/GraphEditor.qml @@ -29,6 +29,9 @@ Item { signal computeRequest(var node) signal submitRequest(var node) + // Files have been dropped + signal filesDropped(var drop, var mousePosition) + // trigger initial fit() after initialization // (ensure GraphEditor has its final size) Component.onCompleted: firstFitTimer.start() @@ -705,6 +708,20 @@ Item { Item { id: boxSelectDraggable } + + DropArea { + id: dropArea + anchors.fill: parent + keys: ["text/uri-list"] + onDropped: { + // retrieve mouse position and convert coordinate system + // from pixel values to graph reference system + var mousePosition = mapToItem(draggable, drag.x, drag.y) + // send the list of files, + // to create the corresponding nodes or open another scene + filesDropped(drop, mousePosition) + } + } } // Toolbar diff --git a/meshroom/ui/qml/ImageGallery/ImageGallery.qml b/meshroom/ui/qml/ImageGallery/ImageGallery.qml index 27a890a8..748853be 100644 --- a/meshroom/ui/qml/ImageGallery/ImageGallery.qml +++ b/meshroom/ui/qml/ImageGallery/ImageGallery.qml @@ -28,6 +28,9 @@ Panel { property int defaultCellSize: 160 property bool readOnly: false + property bool isMeshroomScene : false + property int nbFilesDropped: 0 + signal removeImageRequest(var attribute) signal allViewpointsCleared() signal filesDropped(var drop, var augmentSfm) @@ -438,7 +441,16 @@ Panel { anchors.fill: parent enabled: !m.readOnly && !intrinsicsFilterButton.checked keys: ["text/uri-list"] - // TODO: onEntered: call specific method to filter files based on extension + onEntered: { + isMeshroomScene = false + nbFilesDropped = drag.urls.length + if (nbFilesDropped == 1){ + var url = drag.urls[0] + if (url.endsWith(".mg")){ + isMeshroomScene = true + } + } + } onDropped: { var augmentSfm = augmentArea.hovered root.filesDropped(drop, augmentSfm) @@ -463,7 +475,17 @@ Panel { Layout.fillHeight: true horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter - text: "Add Images" + text: { + if (isMeshroomScene) { + if(nbFilesDropped == 1) { + return "Load Project" + } else { + return "Only one project" + } + }else if (!isMeshroomScene){ + return "Add Images" + } + } font.bold: true background: Rectangle { color: parent.hovered ? parent.palette.highlight : parent.palette.window @@ -483,7 +505,15 @@ Panel { text: "Augment Reconstruction" font.bold: true wrapMode: Text.WrapAtWordBoundaryOrAnywhere - visible: m.viewpoints ? m.viewpoints.count > 0 : false + visible: { + if(isMeshroomScene) + return false + if(m.viewpoints){ + return m.viewpoints.count > 0 + }else{ + return false + } + } background: Rectangle { color: parent.hovered ? palette.highlight : palette.window opacity: 0.8 diff --git a/meshroom/ui/qml/WorkspaceView.qml b/meshroom/ui/qml/WorkspaceView.qml index f772ed52..ecc14431 100644 --- a/meshroom/ui/qml/WorkspaceView.qml +++ b/meshroom/ui/qml/WorkspaceView.qml @@ -80,7 +80,9 @@ Item { cameraInitIndex: reconstruction ? reconstruction.cameraInitIndex : -1 onRemoveImageRequest: reconstruction.removeAttribute(attribute) onAllViewpointsCleared: { reconstruction.removeAllImages(); reconstruction.selectedViewId = "-1" } - onFilesDropped: reconstruction.handleFilesDrop(drop, augmentSfm ? null : cameraInit) + onFilesDropped: { + reconstruction.handleFilesUrl(drop.urls, augmentSfm ? null : cameraInit) + } } LiveSfmView { visible: settings_UILayout.showLiveReconstruction diff --git a/meshroom/ui/qml/main.qml b/meshroom/ui/qml/main.qml index c6261501..17dadc8c 100644 --- a/meshroom/ui/qml/main.qml +++ b/meshroom/ui/qml/main.qml @@ -1186,6 +1186,9 @@ ApplicationWindow { _reconstruction.forceNodesStatusUpdate(); computeManager.submit(node) } + onFilesDropped: { + _reconstruction.handleFilesUrl(drop.urls, null, mousePosition) + } } TaskManager { diff --git a/meshroom/ui/reconstruction.py b/meshroom/ui/reconstruction.py index 781c398d..25aa3a76 100755 --- a/meshroom/ui/reconstruction.py +++ b/meshroom/ui/reconstruction.py @@ -6,7 +6,7 @@ from collections.abc import Iterable from multiprocessing.pool import ThreadPool from threading import Thread -from PySide2.QtCore import QObject, Slot, Property, Signal, QUrl, QSizeF +from PySide2.QtCore import QObject, Slot, Property, Signal, QUrl, QSizeF, QPoint from PySide2.QtGui import QMatrix4x4, QMatrix3x3, QQuaternion, QVector3D, QVector2D import meshroom.core @@ -716,14 +716,24 @@ class Reconstruction(UIGraph): """ Get all view Ids involved in the reconstruction. """ return [vp.viewId.value for node in self._cameraInits for vp in node.viewpoints.value] - @Slot(QObject, Node) - def handleFilesDrop(self, drop, cameraInit): + @Slot('QList') + @Slot('QList', Node) + @Slot('QList', Node, 'QPoint') + def handleFilesUrl(self, urls, cameraInit=None, position=None): """ Handle drop events aiming to add images to the Reconstruction. - Fetching urls from dropEvent is generally expensive in QML/JS (bug ?). This method allows to reduce process time by doing it on Python side. """ - filesByType = self.getFilesByTypeFromDrop(drop) + filesByType = self.getFilesByTypeFromDrop(urls) if filesByType.images: + if cameraInit is None: + boundingBox = self.layout.boundingBox() + if not position: + p = Position(boundingBox[0], boundingBox[1] + boundingBox[3]) + elif isinstance(position, QPoint): + p = Position(position.x(), position.y()) + else: + p = position + cameraInit = self.addNewNode("CameraInit", position=p) self._workerThreads.apply_async(func=self.importImagesSync, args=(filesByType.images, cameraInit,)) if filesByType.videos: boundingBox = self.layout.boundingBox() @@ -773,26 +783,33 @@ class Reconstruction(UIGraph): if not filesByType.images and not filesByType.videos and not filesByType.panoramaInfo: if filesByType.other: - extensions = set([os.path.splitext(url)[1] for url in filesByType.other]) - self.error.emit( - Message( - "No Recognized Input File", - "No recognized input file in the {} dropped files".format(len(filesByType.other)), - "Unknown file extensions: " + ', '.join(extensions) + singleMgFile = False + if len(filesByType.other) == 1: + url = filesByType.other[0] + ext = os.path.splitext(url)[1] + if ext == '.mg': + self.loadUrl(url) + singleMgFile = True + if not singleMgFile: + extensions = set([os.path.splitext(url)[1] for url in filesByType.other]) + self.error.emit( + Message( + "No Recognized Input File", + "No recognized input file in the {} dropped files".format(len(filesByType.other)), + "Unknown file extensions: " + ', '.join(extensions) + ) ) - ) @staticmethod - def getFilesByTypeFromDrop(drop): + def getFilesByTypeFromDrop(urls): """ Args: - drop: + urls: list of filepaths Returns: List of recognized images and list of other files """ - urls = drop.property("urls") # Build the list of images paths filesByType = multiview.FilesByType() for url in urls: