Merge pull request #1004 from jdanielp/fix-olivierkes-950

Fix crash when files are locked for writing (Fixes olivierkes#950)
This commit is contained in:
Tobias Frisch 2022-03-30 22:49:04 +02:00 committed by GitHub
commit daf456e11a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 203 additions and 12 deletions

View file

@ -63,6 +63,6 @@ def loadProject(project):
LOGGER.info("Detected file format version: {}. Zip: {}.".format(version, isZip))
if version == 0:
v0.loadProject(project)
return v0.loadProject(project)
else:
v1.loadProject(project, zip=isZip)
return v1.loadProject(project, zip=isZip)

View file

@ -15,6 +15,7 @@ from collections import OrderedDict
from PyQt5.QtCore import Qt, QModelIndex
from PyQt5.QtGui import QColor, QStandardItem
from PyQt5.QtWidgets import QListWidgetItem
from manuskript import settings
from manuskript.enums import Character, World, Plot, PlotStep, Outline
@ -25,6 +26,7 @@ from lxml import etree as ET
from manuskript.load_save.version_0 import loadFilesFromZip
from manuskript.models.characterModel import CharacterInfo
from manuskript.models import outlineItem
from manuskript.ui.listDialog import ListDialog
import logging
LOGGER = logging.getLogger(__name__)
@ -327,8 +329,8 @@ def saveProject(zip=None):
# Save to plain text
else:
global cache
filesWithPermissionErrors = list()
# Project path
dir = os.path.dirname(project)
@ -378,11 +380,19 @@ def saveProject(zip=None):
LOGGER.debug("* Writing file {} ({})".format(path, "not in cache" if path not in cache else "different"))
# mode = "w" + ("b" if type(content) == bytes else "")
if type(content) == bytes:
with open(filename, "wb") as f:
f.write(content)
try:
with open(filename, "wb") as f:
f.write(content)
except PermissionError as e:
LOGGER.error("Cannot open file " + filename + " for writing: " + e.strerror)
filesWithPermissionErrors.append(filename)
else:
with open(filename, "w", encoding='utf8') as f:
f.write(content)
try:
with open(filename, "w", encoding='utf8') as f:
f.write(content)
except PermissionError as e:
LOGGER.error("Cannot open file " + filename + " for writing: " + e.strerror)
filesWithPermissionErrors.append(filename)
cache[path] = content
@ -412,9 +422,24 @@ def saveProject(zip=None):
pass
# Write the project file's content
with open(project, "w", encoding='utf8') as f:
f.write("1") # Format number
try:
with open(project, "w", encoding='utf8') as f:
f.write("1") # Format number
except PermissionError as e:
LOGGER.error("Cannot open file " + project + " for writing: " + e.strerror)
filesWithPermissionErrors.append(project)
if len(filesWithPermissionErrors) > 0:
dlg = ListDialog(mw)
dlg.setModal(True)
dlg.setWindowTitle(dlg.tr("Files not saved"))
dlg.label.setText(dlg.tr("The following files were not saved and appear to be open in another program"))
for f in filesWithPermissionErrors:
QListWidgetItem(f, dlg.listWidget)
dlg.open()
if project in filesWithPermissionErrors:
return False
return True
@ -622,7 +647,8 @@ def loadProject(project, zip=None):
"""
mw = mainWindow()
errors = []
errors = list()
filesWithPermissionErrors = list()
####################################################################################################################
# Read and store everything in a dict
@ -661,8 +687,14 @@ def loadProject(project, zip=None):
with open(os.path.join(dirpath, f), "rb") as fo:
files[os.path.join(p, f)] = fo.read()
else:
with open(os.path.join(dirpath, f), "r", encoding="utf8") as fo:
files[os.path.join(p, f)] = fo.read()
try:
filename = os.path.join(dirpath, f)
with open(filename, "r", encoding="utf8") as fo:
files[os.path.join(p, f)] = fo.read()
except PermissionError as e:
LOGGER.error("Cannot open file " + filename + ": " + e.strerror)
errors.append(fo)
filesWithPermissionErrors.append(filename)
# Saves to cache (only if we loaded from disk and not zip)
global cache
@ -887,6 +919,15 @@ def loadProject(project, zip=None):
# Check IDS
mdl.rootItem.checkIDs()
if len(filesWithPermissionErrors) > 0:
dlg = ListDialog(mw)
dlg.setModal(True)
dlg.setWindowTitle(dlg.tr("Files not loaded"))
dlg.label.setText(dlg.tr("The following files were not loaded and appear to be open in another program"))
for f in filesWithPermissionErrors:
QListWidgetItem(f, dlg.listWidget)
dlg.open()
return errors

View file

@ -0,0 +1,16 @@
from PyQt5.QtWidgets import QDialog
from manuskript.ui.listDialog_ui import Ui_GenericListDialog
class ListDialog(QDialog, Ui_GenericListDialog):
def __init__(self, parent=None):
QDialog.__init__(self, parent)
self.setupUi(self)
def accept(self):
self.hide()
self.close()
def reject(self):
self.hide()
self.close()

View file

@ -0,0 +1,53 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'listDialog_ui.ui'
#
# Created by: PyQt5 UI code generator 5.12.3
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_GenericListDialog(object):
def setupUi(self, GenericListDialog):
GenericListDialog.setObjectName("GenericListDialog")
GenericListDialog.resize(451, 340)
GenericListDialog.setModal(False)
self.verticalLayout_2 = QtWidgets.QVBoxLayout(GenericListDialog)
self.verticalLayout_2.setObjectName("verticalLayout_2")
self.verticalLayout = QtWidgets.QVBoxLayout()
self.verticalLayout.setObjectName("verticalLayout")
self.label = QtWidgets.QLabel(GenericListDialog)
self.label.setObjectName("label")
self.verticalLayout.addWidget(self.label)
self.listWidget = QtWidgets.QListWidget(GenericListDialog)
self.listWidget.setObjectName("listWidget")
self.verticalLayout.addWidget(self.listWidget)
self.verticalLayout_2.addLayout(self.verticalLayout)
self.buttonBox = QtWidgets.QDialogButtonBox(GenericListDialog)
self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Ok)
self.buttonBox.setObjectName("buttonBox")
self.verticalLayout_2.addWidget(self.buttonBox)
self.retranslateUi(GenericListDialog)
self.buttonBox.accepted.connect(GenericListDialog.accept)
self.buttonBox.rejected.connect(GenericListDialog.reject)
QtCore.QMetaObject.connectSlotsByName(GenericListDialog)
def retranslateUi(self, GenericListDialog):
_translate = QtCore.QCoreApplication.translate
GenericListDialog.setWindowTitle(_translate("GenericListDialog", "Title"))
self.label.setText(_translate("GenericListDialog", "Text"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
GenericListDialog = QtWidgets.QDialog()
ui = Ui_GenericListDialog()
ui.setupUi(GenericListDialog)
GenericListDialog.show()
sys.exit(app.exec_())

View file

@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>GenericListDialog</class>
<widget class="QDialog" name="GenericListDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>451</width>
<height>340</height>
</rect>
</property>
<property name="windowTitle">
<string>Title</string>
</property>
<property name="modal">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Text</string>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="listWidget"/>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>GenericListDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>GenericListDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>