diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 150952c4ff..feb7af7c84 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -49,11 +49,44 @@ from PyQt5.QtGui import QColor, QIcon from PyQt5.QtWidgets import QMessageBox from PyQt5.QtQml import qmlRegisterUncreatableType, qmlRegisterSingletonType, qmlRegisterType +from contextlib import contextmanager + import sys import os.path import numpy import copy import urllib +import os +import time + +CONFIG_LOCK_FILENAME = "cura.lock" + +## Contextmanager to create a lock file and remove it afterwards. +@contextmanager +def lockFile(filename): + try: + with open(filename, 'w') as lock_file: + lock_file.write("Lock file - Cura is currently writing") + except: + Logger.log("e", "Could not create lock file [%s]" % filename) + yield + try: + if os.path.exists(filename): + os.remove(filename) + except: + Logger.log("e", "Could not delete lock file [%s]" % filename) + + +## Wait for a lock file to disappear +# the maximum allowable age is settable; if the file is too old, it will be ignored too +def waitFileDisappear(filename, max_age_seconds=10, msg=""): + now = time.time() + while os.path.exists(filename) and now < os.path.getmtime(filename) + max_age_seconds and now > os.path.getmtime(filename): + if msg: + Logger.log("d", msg) + time.sleep(1) + now = time.time() + numpy.seterr(all="ignore") @@ -201,6 +234,11 @@ class CuraApplication(QtApplication): empty_quality_changes_container.addMetaDataEntry("type", "quality_changes") ContainerRegistry.getInstance().addContainer(empty_quality_changes_container) + # Set the filename to create if cura is writing in the config dir. + self._config_lock_filename = os.path.join(Resources.getConfigStoragePath(), CONFIG_LOCK_FILENAME) + # Lock file check: if (another) Cura is writing in the Config dir, one may not be able to read a + # valid set of files. Not entirely fool-proof, but works when you start Cura shortly after shutting down. + waitFileDisappear(self._config_lock_filename, max_age_seconds=10, msg="Waiting for Cura to finish writing in the config dir...") ContainerRegistry.getInstance().load() Preferences.getInstance().addPreference("cura/active_mode", "simple") @@ -308,38 +346,41 @@ class CuraApplication(QtApplication): if not self._started: # Do not do saving during application start return - for instance in ContainerRegistry.getInstance().findInstanceContainers(): - if not instance.isDirty(): - continue + # When starting Cura, we check for the lockFile which is created and deleted here + with lockFile(self._config_lock_filename): - try: - data = instance.serialize() - except NotImplementedError: - continue - except Exception: - Logger.logException("e", "An exception occurred when serializing container %s", instance.getId()) - continue + for instance in ContainerRegistry.getInstance().findInstanceContainers(): + if not instance.isDirty(): + continue - mime_type = ContainerRegistry.getMimeTypeForContainer(type(instance)) - file_name = urllib.parse.quote_plus(instance.getId()) + "." + mime_type.preferredSuffix - instance_type = instance.getMetaDataEntry("type") - path = None - if instance_type == "material": - path = Resources.getStoragePath(self.ResourceTypes.MaterialInstanceContainer, file_name) - elif instance_type == "quality" or instance_type == "quality_changes": - path = Resources.getStoragePath(self.ResourceTypes.QualityInstanceContainer, file_name) - elif instance_type == "user": - path = Resources.getStoragePath(self.ResourceTypes.UserInstanceContainer, file_name) - elif instance_type == "variant": - path = Resources.getStoragePath(self.ResourceTypes.VariantInstanceContainer, file_name) + try: + data = instance.serialize() + except NotImplementedError: + continue + except Exception: + Logger.logException("e", "An exception occurred when serializing container %s", instance.getId()) + continue - if path: - instance.setPath(path) - with SaveFile(path, "wt", -1, "utf-8") as f: - f.write(data) + mime_type = ContainerRegistry.getMimeTypeForContainer(type(instance)) + file_name = urllib.parse.quote_plus(instance.getId()) + "." + mime_type.preferredSuffix + instance_type = instance.getMetaDataEntry("type") + path = None + if instance_type == "material": + path = Resources.getStoragePath(self.ResourceTypes.MaterialInstanceContainer, file_name) + elif instance_type == "quality" or instance_type == "quality_changes": + path = Resources.getStoragePath(self.ResourceTypes.QualityInstanceContainer, file_name) + elif instance_type == "user": + path = Resources.getStoragePath(self.ResourceTypes.UserInstanceContainer, file_name) + elif instance_type == "variant": + path = Resources.getStoragePath(self.ResourceTypes.VariantInstanceContainer, file_name) - for stack in ContainerRegistry.getInstance().findContainerStacks(): - self.saveStack(stack) + if path: + instance.setPath(path) + with SaveFile(path, "wt", -1, "utf-8") as f: + f.write(data) + + for stack in ContainerRegistry.getInstance().findContainerStacks(): + self.saveStack(stack) def saveStack(self, stack): if not stack.isDirty():