[ui] drag&drop: common behavior for graph editor and image gallery

In Image Gallery :
- drop 1 .mg open the scene
- drop images either create new camera or augment the reconstruction

In Graph Editor :
- drop 1 .mg open the scene
- drop images create new camera at position of mouse
This commit is contained in:
Aurore LAFAURIE 2024-03-20 17:05:28 +01:00
parent bb9661a141
commit 67fbf1b00f
5 changed files with 88 additions and 19 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -1186,6 +1186,9 @@ ApplicationWindow {
_reconstruction.forceNodesStatusUpdate();
computeManager.submit(node)
}
onFilesDropped: {
_reconstruction.handleFilesUrl(drop.urls, null, mousePosition)
}
}
TaskManager {

View file

@ -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<QUrl>')
@Slot('QList<QUrl>', Node)
@Slot('QList<QUrl>', 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,6 +783,14 @@ class Reconstruction(UIGraph):
if not filesByType.images and not filesByType.videos and not filesByType.panoramaInfo:
if filesByType.other:
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(
@ -783,16 +801,15 @@ class Reconstruction(UIGraph):
)
@staticmethod
def getFilesByTypeFromDrop(drop):
def getFilesByTypeFromDrop(urls):
"""
Args:
drop:
urls: list of filepaths
Returns:
<images, otherFiles> 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: