mirror of
https://github.com/alicevision/Meshroom.git
synced 2025-04-28 17:57:16 +02:00
Automatically save the project when computing or submitting to renderfarm
If the project is not saved at all, it will suggest to save it manually or to define a project in a temporary folder using date/time for the project name.
This commit is contained in:
parent
db8fd02aeb
commit
008d6c75ee
6 changed files with 53 additions and 44 deletions
|
@ -31,7 +31,6 @@ logging.basicConfig(format='[%(asctime)s][%(levelname)s] %(message)s', level=log
|
|||
sessionUid = str(uuid.uuid1())
|
||||
|
||||
cacheFolderName = 'MeshroomCache'
|
||||
defaultCacheFolder = os.environ.get('MESHROOM_CACHE', os.path.join(tempfile.gettempdir(), cacheFolderName))
|
||||
nodesDesc = {}
|
||||
submitters = {}
|
||||
pipelineTemplates = {}
|
||||
|
|
|
@ -25,18 +25,6 @@ class MrNodeType(enum.Enum):
|
|||
COMMANDLINE = enum.auto()
|
||||
INPUT = enum.auto()
|
||||
|
||||
def isNodeSaved(node):
|
||||
"""Returns whether a node is identical to its serialized counterpart in the current graph file."""
|
||||
filepath = node.graph.filepath
|
||||
if not filepath:
|
||||
return False
|
||||
|
||||
from meshroom.core.graph import loadGraph
|
||||
graphSaved = loadGraph(filepath)
|
||||
nodeSaved = graphSaved.node(node.name)
|
||||
if nodeSaved is None:
|
||||
return False
|
||||
return nodeSaved._uid == node._uid
|
||||
|
||||
class BaseNode(object):
|
||||
"""
|
||||
|
@ -259,9 +247,6 @@ class Node(BaseNode):
|
|||
return MrNodeType.NODE
|
||||
|
||||
def processChunkInEnvironment(self, chunk):
|
||||
if not isNodeSaved(chunk.node):
|
||||
raise RuntimeError("File must be saved before computing in isolated environment.")
|
||||
|
||||
meshroomComputeCmd = f"python {_MESHROOM_COMPUTE} {chunk.node.graph.filepath} --node {chunk.node.name} --extern --inCurrentEnv"
|
||||
if len(chunk.node.getChunks()) > 1:
|
||||
meshroomComputeCmd += f" --iteration {chunk.range.iteration}"
|
||||
|
|
|
@ -182,7 +182,6 @@ class Graph(BaseObject):
|
|||
edges = {B.input: A.output, C.input: B.output,}
|
||||
|
||||
"""
|
||||
_cacheDir = ""
|
||||
|
||||
def __init__(self, name, parent=None):
|
||||
super(Graph, self).__init__(parent)
|
||||
|
@ -199,7 +198,7 @@ class Graph(BaseObject):
|
|||
# Edges: use dst attribute as unique key since it can only have one input connection
|
||||
self._edges = DictModel(keyAttrName='dst', parent=self)
|
||||
self._compatibilityNodes = DictModel(keyAttrName='name', parent=self)
|
||||
self.cacheDir = meshroom.core.defaultCacheFolder
|
||||
self._cacheDir = ''
|
||||
self._filepath = ''
|
||||
self._fileDateVersion = 0
|
||||
self.header = {}
|
||||
|
@ -1354,7 +1353,7 @@ class Graph(BaseObject):
|
|||
def _unsetFilepath(self):
|
||||
self._filepath = ""
|
||||
self.name = ""
|
||||
self.cacheDir = meshroom.core.defaultCacheFolder
|
||||
self.cacheDir = ""
|
||||
self.filepathChanged.emit()
|
||||
|
||||
def updateInternals(self, startNodes=None, force=False):
|
||||
|
|
|
@ -12,9 +12,9 @@ import os
|
|||
from dataclasses import dataclass
|
||||
from enum import Enum
|
||||
import sys
|
||||
import tempfile
|
||||
from typing import Any, Type
|
||||
|
||||
|
||||
meshroomFolder = os.path.dirname(__file__)
|
||||
|
||||
@dataclass
|
||||
|
@ -46,6 +46,8 @@ class EnvVar(Enum):
|
|||
MESHROOM_NODES_PATH = VarDefinition(str, "", "Paths to set of nodes folders")
|
||||
MESHROOM_SUBMITTERS_PATH = VarDefinition(str, "", "Paths to set of submitters folders")
|
||||
MESHROOM_PIPELINE_TEMPLATES_PATH = VarDefinition(str, "", "Paths to et of pipeline templates folders")
|
||||
MESHROOM_TEMP_PATH = VarDefinition(str, tempfile.gettempdir(), "Path to the temporary folder")
|
||||
|
||||
|
||||
@staticmethod
|
||||
def get(envVar: "EnvVar") -> Any:
|
||||
|
|
|
@ -514,6 +514,14 @@ class UIGraph(QObject):
|
|||
# => force re-evaluation of monitored status files paths
|
||||
self.updateChunkMonitor(self._sortedDFSChunks)
|
||||
|
||||
@Slot()
|
||||
def saveAsTemp(self):
|
||||
from meshroom.env import EnvVar
|
||||
from datetime import datetime
|
||||
tempFolder = EnvVar.get(EnvVar.MESHROOM_TEMP_PATH)
|
||||
timestamp = datetime.now().strftime("%Y-%m-%d_%H:%M")
|
||||
self._saveAs(os.path.join(tempFolder, f"meshroom_{timestamp}.mg"))
|
||||
|
||||
@Slot()
|
||||
def save(self):
|
||||
self._graph.save()
|
||||
|
@ -531,6 +539,7 @@ class UIGraph(QObject):
|
|||
@Slot(list)
|
||||
def execute(self, nodes: Optional[Union[list[Node], Node]] = None):
|
||||
nodes = [nodes] if not isinstance(nodes, Iterable) and nodes else nodes
|
||||
self.save() # always save the graph before computing
|
||||
self._taskManager.compute(self._graph, nodes)
|
||||
self.updateLockedUndoStack() # explicitly call the update while it is already computing
|
||||
|
||||
|
|
|
@ -134,6 +134,8 @@ Page {
|
|||
id: saveFileDialog
|
||||
options: Platform.FileDialog.DontUseNativeDialog
|
||||
|
||||
property var _callback: undefined
|
||||
|
||||
signal closed(var result)
|
||||
|
||||
title: "Save File"
|
||||
|
@ -150,8 +152,28 @@ Page {
|
|||
_reconstruction.saveAs(currentFile)
|
||||
MeshroomApp.addRecentProjectFile(currentFile.toString())
|
||||
closed(Platform.Dialog.Accepted)
|
||||
fireCallback(Platform.Dialog.Accepted)
|
||||
}
|
||||
onRejected: {
|
||||
closed(Platform.Dialog.Rejected)
|
||||
fireCallback(Platform.Dialog.Rejected)
|
||||
}
|
||||
|
||||
function fireCallback(rc)
|
||||
{
|
||||
// Call the callback and reset it
|
||||
if (_callback)
|
||||
_callback(rc)
|
||||
_callback = undefined
|
||||
}
|
||||
|
||||
// Open the unsaved dialog warning with an optional
|
||||
// callback to fire when the dialog is accepted/discarded
|
||||
function prompt(callback)
|
||||
{
|
||||
_callback = callback
|
||||
open()
|
||||
}
|
||||
onRejected: closed(Platform.Dialog.Rejected)
|
||||
}
|
||||
|
||||
Platform.FileDialog {
|
||||
|
@ -218,8 +240,6 @@ Page {
|
|||
Item {
|
||||
id: computeManager
|
||||
|
||||
property bool warnIfUnsaved: true
|
||||
|
||||
// Evaluate if graph computation can be submitted externally
|
||||
property bool canSubmit: _reconstruction ?
|
||||
_reconstruction.canSubmit // current setup allows to compute externally
|
||||
|
@ -227,7 +247,7 @@ Page {
|
|||
false
|
||||
|
||||
function compute(nodes, force) {
|
||||
if (!force && warnIfUnsaved && !_reconstruction.graph.filepath) {
|
||||
if (!force && !_reconstruction.graph.filepath) {
|
||||
unsavedComputeDialog.selectedNodes = nodes;
|
||||
unsavedComputeDialog.open();
|
||||
}
|
||||
|
@ -339,29 +359,28 @@ Page {
|
|||
parent: Overlay.overlay
|
||||
preset: "Warning"
|
||||
title: "Unsaved Project"
|
||||
text: "Data will be computed in the default cache folder if project remains unsaved."
|
||||
detailedText: "Default cache folder: " + (_reconstruction ? _reconstruction.graph.cacheDir : "unknown")
|
||||
helperText: "Save project first?"
|
||||
text: "Saving the project is required."
|
||||
helperText: "Choose a location to save the project, or use the default temporary path."
|
||||
standardButtons: Dialog.Discard | Dialog.Cancel | Dialog.Save
|
||||
|
||||
CheckBox {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
text: "Don't ask again for this session"
|
||||
padding: 0
|
||||
onToggled: computeManager.warnIfUnsaved = !checked
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
// Set up discard button text
|
||||
standardButton(Dialog.Discard).text = "Continue without Saving"
|
||||
standardButton(Dialog.Discard).text = "Continue in Temp Folder"
|
||||
standardButton(Dialog.Save).text = "Save As"
|
||||
}
|
||||
|
||||
onDiscarded: {
|
||||
_reconstruction.saveAsTemp()
|
||||
close()
|
||||
computeManager.compute(selectedNodes, true)
|
||||
}
|
||||
|
||||
onAccepted: saveAsAction.trigger()
|
||||
onAccepted: {
|
||||
initFileDialogFolder(saveFileDialog)
|
||||
saveFileDialog.prompt(function(rc) {
|
||||
computeManager.compute(selectedNodes, true)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
MessageDialog {
|
||||
|
@ -444,14 +463,10 @@ Page {
|
|||
}
|
||||
// Open "Save As" dialog
|
||||
else {
|
||||
saveFileDialog.open()
|
||||
function _callbackWrapper(rc) {
|
||||
saveFileDialog.prompt(function(rc) {
|
||||
if (rc === Platform.Dialog.Accepted)
|
||||
fireCallback()
|
||||
|
||||
saveFileDialog.closed.disconnect(_callbackWrapper)
|
||||
}
|
||||
saveFileDialog.closed.connect(_callbackWrapper)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -463,8 +478,8 @@ Page {
|
|||
_callback = undefined
|
||||
}
|
||||
|
||||
/// Open the unsaved dialog warning with an optional
|
||||
/// callback to fire when the dialog is accepted/discarded
|
||||
// Open the unsaved dialog warning with an optional
|
||||
// callback to fire when the dialog is accepted/discarded
|
||||
function prompt(callback)
|
||||
{
|
||||
_callback = callback
|
||||
|
|
Loading…
Add table
Reference in a new issue