[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:
Fabien Castan 2020-03-16 19:58:37 +01:00
parent dcf91c244e
commit 0e434908a5
6 changed files with 101 additions and 19 deletions

View file

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

View file

@ -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):
""" """

View file

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

View file

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

View file

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

View file

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