mirror of
https://github.com/alicevision/Meshroom.git
synced 2025-07-23 19:47:39 +02:00
[ui] improve open recent files
* fix path conversion on windows * remove invalid paths from the list on error * explicit error message for "No Such File"
This commit is contained in:
parent
dcf91c244e
commit
0e434908a5
6 changed files with 101 additions and 19 deletions
|
@ -280,6 +280,8 @@ class Graph(BaseObject):
|
||||||
# Create graph edges by resolving attributes expressions
|
# Create graph edges by resolving attributes expressions
|
||||||
self._applyExpr()
|
self._applyExpr()
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def updateEnabled(self):
|
def updateEnabled(self):
|
||||||
return self._updateEnabled
|
return self._updateEnabled
|
||||||
|
|
|
@ -8,6 +8,8 @@ from PySide2.QtWidgets import QApplication
|
||||||
|
|
||||||
import meshroom
|
import meshroom
|
||||||
from meshroom.core import nodesDesc
|
from meshroom.core import nodesDesc
|
||||||
|
from meshroom.core import pyCompatibility
|
||||||
|
|
||||||
from meshroom.ui import components
|
from meshroom.ui import components
|
||||||
from meshroom.ui.components.clipboard import ClipboardHelper
|
from meshroom.ui.components.clipboard import ClipboardHelper
|
||||||
from meshroom.ui.components.filepath import FilepathHelper
|
from meshroom.ui.components.filepath import FilepathHelper
|
||||||
|
@ -183,8 +185,19 @@ class MeshroomApp(QApplication):
|
||||||
return projects
|
return projects
|
||||||
|
|
||||||
@Slot(str)
|
@Slot(str)
|
||||||
|
@Slot(QUrl)
|
||||||
def addRecentProjectFile(self, projectFile):
|
def addRecentProjectFile(self, projectFile):
|
||||||
projectFile = QUrl(projectFile).path()
|
if not isinstance(projectFile, (QUrl, pyCompatibility.basestring)):
|
||||||
|
raise TypeError("Unexpected data type: {}".format(projectFile.__class__))
|
||||||
|
if isinstance(projectFile, QUrl):
|
||||||
|
projectFileNorm = projectFile.toLocalFile()
|
||||||
|
if not projectFileNorm:
|
||||||
|
projectFileNorm = projectFile.toString()
|
||||||
|
else:
|
||||||
|
projectFileNorm = QUrl(projectFile).toLocalFile()
|
||||||
|
if not projectFileNorm:
|
||||||
|
projectFileNorm = QUrl.fromLocalFile(projectFile).toLocalFile()
|
||||||
|
|
||||||
projects = self._recentProjectFiles()
|
projects = self._recentProjectFiles()
|
||||||
|
|
||||||
# remove duplicates while preserving order
|
# remove duplicates while preserving order
|
||||||
|
@ -192,10 +205,10 @@ class MeshroomApp(QApplication):
|
||||||
uniqueProjects = OrderedDict.fromkeys(projects)
|
uniqueProjects = OrderedDict.fromkeys(projects)
|
||||||
projects = list(uniqueProjects)
|
projects = list(uniqueProjects)
|
||||||
# remove previous usage of the value
|
# remove previous usage of the value
|
||||||
if projectFile in uniqueProjects:
|
if projectFileNorm in uniqueProjects:
|
||||||
projects.remove(projectFile)
|
projects.remove(projectFileNorm)
|
||||||
# add the new value in the first place
|
# add the new value in the first place
|
||||||
projects.insert(0, projectFile)
|
projects.insert(0, projectFileNorm)
|
||||||
|
|
||||||
# keep only the 10 first elements
|
# keep only the 10 first elements
|
||||||
projects = projects[0:20]
|
projects = projects[0:20]
|
||||||
|
@ -211,6 +224,43 @@ class MeshroomApp(QApplication):
|
||||||
|
|
||||||
self.recentProjectFilesChanged.emit()
|
self.recentProjectFilesChanged.emit()
|
||||||
|
|
||||||
|
@Slot(str)
|
||||||
|
@Slot(QUrl)
|
||||||
|
def removeRecentProjectFile(self, projectFile):
|
||||||
|
if not isinstance(projectFile, (QUrl, pyCompatibility.basestring)):
|
||||||
|
raise TypeError("Unexpected data type: {}".format(projectFile.__class__))
|
||||||
|
if isinstance(projectFile, QUrl):
|
||||||
|
projectFileNorm = projectFile.toLocalFile()
|
||||||
|
if not projectFileNorm:
|
||||||
|
projectFileNorm = projectFile.toString()
|
||||||
|
else:
|
||||||
|
projectFileNorm = QUrl(projectFile).toLocalFile()
|
||||||
|
if not projectFileNorm:
|
||||||
|
projectFileNorm = QUrl.fromLocalFile(projectFile).toLocalFile()
|
||||||
|
|
||||||
|
projects = self._recentProjectFiles()
|
||||||
|
|
||||||
|
# remove duplicates while preserving order
|
||||||
|
from collections import OrderedDict
|
||||||
|
uniqueProjects = OrderedDict.fromkeys(projects)
|
||||||
|
projects = list(uniqueProjects)
|
||||||
|
# remove previous usage of the value
|
||||||
|
if projectFileNorm not in uniqueProjects:
|
||||||
|
return
|
||||||
|
|
||||||
|
projects.remove(projectFileNorm)
|
||||||
|
|
||||||
|
settings = QSettings()
|
||||||
|
settings.beginGroup("RecentFiles")
|
||||||
|
size = settings.beginWriteArray("Projects")
|
||||||
|
for i, p in enumerate(projects):
|
||||||
|
settings.setArrayIndex(i)
|
||||||
|
settings.setValue("filepath", p)
|
||||||
|
settings.endArray()
|
||||||
|
settings.sync()
|
||||||
|
|
||||||
|
self.recentProjectFilesChanged.emit()
|
||||||
|
|
||||||
@Slot(str, result=str)
|
@Slot(str, result=str)
|
||||||
def markdownToHtml(self, md):
|
def markdownToHtml(self, md):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -309,16 +309,14 @@ class UIGraph(QObject):
|
||||||
self.stopExecution()
|
self.stopExecution()
|
||||||
self._chunksMonitor.stop()
|
self._chunksMonitor.stop()
|
||||||
|
|
||||||
def load(self, filepath, setupProjectFile=True):
|
@Slot(str, result=bool)
|
||||||
|
def loadGraph(self, filepath, setupProjectFile=True):
|
||||||
g = Graph('')
|
g = Graph('')
|
||||||
g.load(filepath, setupProjectFile)
|
status = g.load(filepath, setupProjectFile)
|
||||||
if not os.path.exists(g.cacheDir):
|
if not os.path.exists(g.cacheDir):
|
||||||
os.mkdir(g.cacheDir)
|
os.mkdir(g.cacheDir)
|
||||||
self.setGraph(g)
|
self.setGraph(g)
|
||||||
|
return status
|
||||||
@Slot(QUrl)
|
|
||||||
def loadUrl(self, url):
|
|
||||||
self.load(url.toLocalFile())
|
|
||||||
|
|
||||||
@Slot(QUrl)
|
@Slot(QUrl)
|
||||||
def saveAs(self, url):
|
def saveAs(self, url):
|
||||||
|
|
|
@ -75,7 +75,7 @@ FloatingPane {
|
||||||
property var viewer: root.featuresViewer.itemAt(index)
|
property var viewer: root.featuresViewer.itemAt(index)
|
||||||
spacing: 4
|
spacing: 4
|
||||||
|
|
||||||
// Visibility toogle
|
// Visibility toggle
|
||||||
MaterialToolButton {
|
MaterialToolButton {
|
||||||
text: featureType.viewer.visible ? MaterialIcons.visibility : MaterialIcons.visibility_off
|
text: featureType.viewer.visible ? MaterialIcons.visibility : MaterialIcons.visibility_off
|
||||||
onClicked: featureType.viewer.visible = !featureType.viewer.visible
|
onClicked: featureType.viewer.visible = !featureType.viewer.visible
|
||||||
|
|
|
@ -207,8 +207,10 @@ ApplicationWindow {
|
||||||
title: "Open File"
|
title: "Open File"
|
||||||
nameFilters: ["Meshroom Graphs (*.mg)"]
|
nameFilters: ["Meshroom Graphs (*.mg)"]
|
||||||
onAccepted: {
|
onAccepted: {
|
||||||
_reconstruction.loadUrl(file.toString())
|
if(_reconstruction.loadUrl(file))
|
||||||
MeshroomApp.addRecentProjectFile(file.toString())
|
{
|
||||||
|
MeshroomApp.addRecentProjectFile(file.toString())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -353,8 +355,14 @@ ApplicationWindow {
|
||||||
MenuItem {
|
MenuItem {
|
||||||
onTriggered: ensureSaved(function() {
|
onTriggered: ensureSaved(function() {
|
||||||
openRecentMenu.dismiss();
|
openRecentMenu.dismiss();
|
||||||
_reconstruction.load(modelData);
|
if(_reconstruction.loadUrl(modelData))
|
||||||
MeshroomApp.addRecentProjectFile(modelData);
|
{
|
||||||
|
MeshroomApp.addRecentProjectFile(modelData);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MeshroomApp.removeRecentProjectFile(modelData);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
text: fileTextMetrics.elidedText
|
text: fileTextMetrics.elidedText
|
||||||
|
|
|
@ -430,10 +430,10 @@ class Reconstruction(UIGraph):
|
||||||
# use the user-provided default photogrammetry project file
|
# use the user-provided default photogrammetry project file
|
||||||
self.load(p, setupProjectFile=False)
|
self.load(p, setupProjectFile=False)
|
||||||
|
|
||||||
@Slot(str)
|
@Slot(str, result=bool)
|
||||||
def load(self, filepath, setupProjectFile=True):
|
def load(self, filepath, setupProjectFile=True):
|
||||||
try:
|
try:
|
||||||
super(Reconstruction, self).load(filepath, setupProjectFile)
|
status = super(Reconstruction, self).loadGraph(filepath, setupProjectFile)
|
||||||
# warn about pre-release projects being automatically upgraded
|
# warn about pre-release projects being automatically upgraded
|
||||||
if Version(self._graph.fileReleaseVersion).major == "0":
|
if Version(self._graph.fileReleaseVersion).major == "0":
|
||||||
self.warning.emit(Message(
|
self.warning.emit(Message(
|
||||||
|
@ -442,17 +442,41 @@ class Reconstruction(UIGraph):
|
||||||
"Data might have been lost in the process.",
|
"Data might have been lost in the process.",
|
||||||
"Open it with the corresponding version of Meshroom to recover your data."
|
"Open it with the corresponding version of Meshroom to recover your data."
|
||||||
))
|
))
|
||||||
|
return status
|
||||||
|
except FileNotFoundError as e:
|
||||||
|
self.error.emit(
|
||||||
|
Message(
|
||||||
|
"No Such File",
|
||||||
|
"Error While Loading '{}': No Such File.".format(os.path.basename(filepath)),
|
||||||
|
""
|
||||||
|
)
|
||||||
|
)
|
||||||
|
logging.error("Error while loading '{}': No Such File.".format(os.path.basename(filepath)))
|
||||||
|
return False
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
import traceback
|
import traceback
|
||||||
trace = traceback.format_exc()
|
trace = traceback.format_exc()
|
||||||
self.error.emit(
|
self.error.emit(
|
||||||
Message(
|
Message(
|
||||||
"Error while loading {}".format(os.path.basename(filepath)),
|
"Error While Loading Project File",
|
||||||
"An unexpected error has occurred",
|
"An unexpected error has occurred while loading file: '{}'".format(os.path.basename(filepath)),
|
||||||
trace
|
trace
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
logging.error(trace)
|
logging.error(trace)
|
||||||
|
return False
|
||||||
|
|
||||||
|
@Slot(QUrl, result=bool)
|
||||||
|
def loadUrl(self, url):
|
||||||
|
if isinstance(url, (QUrl)):
|
||||||
|
# depending how the QUrl has been initialized,
|
||||||
|
# toLocalFile() may return the local path or an empty string
|
||||||
|
localFile = url.toLocalFile()
|
||||||
|
if not localFile:
|
||||||
|
localFile = url.toString()
|
||||||
|
else:
|
||||||
|
localFile = url
|
||||||
|
return self.load(localFile)
|
||||||
|
|
||||||
def onGraphChanged(self):
|
def onGraphChanged(self):
|
||||||
""" React to the change of the internal graph. """
|
""" React to the change of the internal graph. """
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue