Improve dialog memory handling (#1899)

This commit is contained in:
Veronica Berglyd Olsen
2024-05-28 23:19:39 +02:00
committed by GitHub
30 changed files with 202 additions and 169 deletions
+8 -13
View File
@@ -33,14 +33,14 @@ from PyQt5.QtWidgets import (
from novelwriter import CONFIG, SHARED from novelwriter import CONFIG, SHARED
from novelwriter.common import cssCol, readTextFile from novelwriter.common import cssCol, readTextFile
from novelwriter.extensions.configlayout import NColourLabel from novelwriter.extensions.configlayout import NColourLabel
from novelwriter.extensions.modified import NNonBlockingDialog from novelwriter.extensions.modified import NDialog
from novelwriter.extensions.versioninfo import VersionInfoWidget from novelwriter.extensions.versioninfo import VersionInfoWidget
from novelwriter.types import QtAlignRightTop, QtDialogClose from novelwriter.types import QtAlignRightTop, QtDialogClose
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class GuiAbout(NNonBlockingDialog): class GuiAbout(NDialog):
def __init__(self, parent: QWidget) -> None: def __init__(self, parent: QWidget) -> None:
super().__init__(parent=parent) super().__init__(parent=parent)
@@ -106,7 +106,9 @@ class GuiAbout(NNonBlockingDialog):
self.setLayout(self.outerBox) self.setLayout(self.outerBox)
self.setSizeGripEnabled(True) self.setSizeGripEnabled(True)
self._setStyleSheet() self._setStyleSheet()
self._fillCreditsPage()
logger.debug("Ready: GuiAbout") logger.debug("Ready: GuiAbout")
@@ -116,11 +118,6 @@ class GuiAbout(NNonBlockingDialog):
logger.debug("Delete: GuiAbout") logger.debug("Delete: GuiAbout")
return return
def populateGUI(self) -> None:
"""Populate tabs with text."""
self._fillCreditsPage()
return
## ##
# Events # Events
## ##
@@ -128,7 +125,7 @@ class GuiAbout(NNonBlockingDialog):
def closeEvent(self, event: QCloseEvent) -> None: def closeEvent(self, event: QCloseEvent) -> None:
"""Capture the close event and perform cleanup.""" """Capture the close event and perform cleanup."""
event.accept() event.accept()
self.deleteLater() self.softDelete()
return return
## ##
@@ -137,16 +134,14 @@ class GuiAbout(NNonBlockingDialog):
def _fillCreditsPage(self) -> None: def _fillCreditsPage(self) -> None:
"""Load the content for the Credits page.""" """Load the content for the Credits page."""
docPath = CONFIG.assetPath("text") / "credits_en.htm" if html := readTextFile(CONFIG.assetPath("text") / "credits_en.htm"):
docText = readTextFile(docPath) self.txtCredits.setHtml(html)
if docText:
self.txtCredits.setHtml(docText)
else: else:
self.txtCredits.setHtml("Error loading credits text ...") self.txtCredits.setHtml("Error loading credits text ...")
return return
def _setStyleSheet(self) -> None: def _setStyleSheet(self) -> None:
"""Set stylesheet for all browser tabs.""" """Set stylesheet text document."""
baseCol = cssCol(self.palette().window().color()) baseCol = cssCol(self.palette().window().color())
self.txtCredits.setStyleSheet( self.txtCredits.setStyleSheet(
f"QTextBrowser {{border: none; background: {baseCol};}} " f"QTextBrowser {{border: none; background: {baseCol};}} "
+15 -15
View File
@@ -27,21 +27,21 @@ from __future__ import annotations
import logging import logging
from PyQt5.QtCore import Qt, pyqtSlot from PyQt5.QtCore import Qt, pyqtSlot
from PyQt5.QtGui import QCloseEvent
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
QAbstractItemView, QDialog, QDialogButtonBox, QGridLayout, QLabel, QAbstractItemView, QDialogButtonBox, QGridLayout, QLabel, QListWidget,
QListWidget, QListWidgetItem, QVBoxLayout, QWidget QListWidgetItem, QVBoxLayout, QWidget
) )
from novelwriter import CONFIG, SHARED from novelwriter import CONFIG, SHARED
from novelwriter.extensions.configlayout import NColourLabel from novelwriter.extensions.configlayout import NColourLabel
from novelwriter.extensions.modified import NDialog
from novelwriter.extensions.switch import NSwitch from novelwriter.extensions.switch import NSwitch
from novelwriter.types import QtDialogCancel, QtDialogOk, QtDialogReset, QtUserRole from novelwriter.types import QtAccepted, QtDialogCancel, QtDialogOk, QtDialogReset, QtUserRole
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class GuiDocMerge(QDialog): class GuiDocMerge(NDialog):
D_HANDLE = QtUserRole D_HANDLE = QtUserRole
@@ -117,7 +117,7 @@ class GuiDocMerge(QDialog):
logger.debug("Delete: GuiDocMerge") logger.debug("Delete: GuiDocMerge")
return return
def getData(self) -> dict: def data(self) -> dict:
"""Return the user's choices.""" """Return the user's choices."""
finalItems = [] finalItems = []
for i in range(self.listBox.count()): for i in range(self.listBox.count()):
@@ -130,15 +130,15 @@ class GuiDocMerge(QDialog):
return self._data return self._data
## @classmethod
# Events def getData(cls, parent: QWidget, handle: str, items: list[str]) -> tuple[dict, bool]:
## """Pop the dialog and return the result."""
cls = GuiDocMerge(parent, handle, items)
def closeEvent(self, event: QCloseEvent) -> None: cls.exec()
"""Capture the close event and perform cleanup.""" data = cls.data()
event.accept() accepted = cls.result() == QtAccepted
self.deleteLater() cls.softDelete()
return return data, accepted
## ##
# Private Slots # Private Slots
+15 -15
View File
@@ -27,21 +27,21 @@ from __future__ import annotations
import logging import logging
from PyQt5.QtCore import pyqtSlot from PyQt5.QtCore import pyqtSlot
from PyQt5.QtGui import QCloseEvent
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
QAbstractItemView, QComboBox, QDialog, QDialogButtonBox, QGridLayout, QAbstractItemView, QComboBox, QDialogButtonBox, QGridLayout, QLabel,
QLabel, QListWidget, QListWidgetItem, QVBoxLayout, QWidget QListWidget, QListWidgetItem, QVBoxLayout, QWidget
) )
from novelwriter import CONFIG, SHARED from novelwriter import CONFIG, SHARED
from novelwriter.extensions.configlayout import NColourLabel from novelwriter.extensions.configlayout import NColourLabel
from novelwriter.extensions.modified import NDialog
from novelwriter.extensions.switch import NSwitch from novelwriter.extensions.switch import NSwitch
from novelwriter.types import QtDialogCancel, QtDialogOk, QtUserRole from novelwriter.types import QtAccepted, QtDialogCancel, QtDialogOk, QtUserRole
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class GuiDocSplit(QDialog): class GuiDocSplit(NDialog):
LINE_ROLE = QtUserRole LINE_ROLE = QtUserRole
LEVEL_ROLE = QtUserRole + 1 LEVEL_ROLE = QtUserRole + 1
@@ -145,7 +145,7 @@ class GuiDocSplit(QDialog):
logger.debug("Delete: GuiDocSplit") logger.debug("Delete: GuiDocSplit")
return return
def getData(self) -> tuple[dict, list]: def data(self) -> tuple[dict, list[str]]:
"""Return the user's choices. Also save the users options for """Return the user's choices. Also save the users options for
the next time the dialog is used. the next time the dialog is used.
""" """
@@ -178,15 +178,15 @@ class GuiDocSplit(QDialog):
return self._data, self._text return self._data, self._text
## @classmethod
# Events def getData(cls, parent: QWidget, handle: str) -> tuple[dict, list[str], bool]:
## """Pop the dialog and return the result."""
cls = GuiDocSplit(parent, handle)
def closeEvent(self, event: QCloseEvent) -> None: cls.exec()
"""Capture the close event and perform cleanup.""" data, text = cls.data()
event.accept() accepted = cls.result() == QtAccepted
self.deleteLater() cls.softDelete()
return return data, text, accepted
## ##
# Private Slots # Private Slots
+6 -8
View File
@@ -25,18 +25,16 @@ from __future__ import annotations
import logging import logging
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import QDialogButtonBox, QHBoxLayout, QLabel, QLineEdit, QVBoxLayout, QWidget
QDialog, QDialogButtonBox, QHBoxLayout, QLabel, QLineEdit, QVBoxLayout,
QWidget
)
from novelwriter import CONFIG from novelwriter import CONFIG
from novelwriter.types import QtDialogCancel, QtDialogOk from novelwriter.extensions.modified import NDialog
from novelwriter.types import QtAccepted, QtDialogCancel, QtDialogOk
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class GuiEditLabel(QDialog): class GuiEditLabel(NDialog):
def __init__(self, parent: QWidget, text: str = "") -> None: def __init__(self, parent: QWidget, text: str = "") -> None:
super().__init__(parent=parent) super().__init__(parent=parent)
@@ -91,6 +89,6 @@ class GuiEditLabel(QDialog):
cls = GuiEditLabel(parent, text=text) cls = GuiEditLabel(parent, text=text)
cls.exec() cls.exec()
label = cls.itemLabel label = cls.itemLabel
accepted = cls.result() == QDialog.DialogCode.Accepted accepted = cls.result() == QtAccepted
cls.deleteLater() cls.softDelete()
return label, accepted return label, accepted
+8 -13
View File
@@ -27,10 +27,10 @@ from __future__ import annotations
import logging import logging
from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot
from PyQt5.QtGui import QCloseEvent, QKeyEvent, QKeySequence from PyQt5.QtGui import QCloseEvent
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
QAbstractButton, QApplication, QCompleter, QDialog, QDialogButtonBox, QAbstractButton, QApplication, QCompleter, QDialogButtonBox, QFileDialog,
QFileDialog, QHBoxLayout, QLineEdit, QPushButton, QVBoxLayout, QWidget QHBoxLayout, QLineEdit, QPushButton, QVBoxLayout, QWidget
) )
from novelwriter import CONFIG, SHARED from novelwriter import CONFIG, SHARED
@@ -38,7 +38,9 @@ from novelwriter.common import describeFont
from novelwriter.constants import nwConst, nwUnicode from novelwriter.constants import nwConst, nwUnicode
from novelwriter.dialogs.quotes import GuiQuoteSelect from novelwriter.dialogs.quotes import GuiQuoteSelect
from novelwriter.extensions.configlayout import NColourLabel, NScrollableForm from novelwriter.extensions.configlayout import NColourLabel, NScrollableForm
from novelwriter.extensions.modified import NComboBox, NDoubleSpinBox, NIconToolButton, NSpinBox from novelwriter.extensions.modified import (
NComboBox, NDialog, NDoubleSpinBox, NIconToolButton, NSpinBox
)
from novelwriter.extensions.pagedsidebar import NPagedSideBar from novelwriter.extensions.pagedsidebar import NPagedSideBar
from novelwriter.extensions.switch import NSwitch from novelwriter.extensions.switch import NSwitch
from novelwriter.types import ( from novelwriter.types import (
@@ -49,7 +51,7 @@ from novelwriter.types import (
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class GuiPreferences(QDialog): class GuiPreferences(NDialog):
newPreferencesReady = pyqtSignal(bool, bool, bool, bool) newPreferencesReady = pyqtSignal(bool, bool, bool, bool)
@@ -769,14 +771,7 @@ class GuiPreferences(QDialog):
event.accept() event.accept()
QApplication.processEvents() QApplication.processEvents()
self.done(nwConst.DLG_FINISHED) self.done(nwConst.DLG_FINISHED)
self.deleteLater() self.softDelete()
return
def keyPressEvent(self, event: QKeyEvent) -> None:
"""Overload keyPressEvent to block enter key to save."""
if event.matches(QKeySequence.StandardKey.Cancel):
self.close()
event.ignore()
return return
## ##
+4 -4
View File
@@ -29,7 +29,7 @@ import logging
from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot
from PyQt5.QtGui import QCloseEvent, QColor from PyQt5.QtGui import QCloseEvent, QColor
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
QAbstractItemView, QApplication, QColorDialog, QDialog, QDialogButtonBox, QAbstractItemView, QApplication, QColorDialog, QDialogButtonBox,
QHBoxLayout, QLineEdit, QMenu, QStackedWidget, QToolButton, QTreeWidget, QHBoxLayout, QLineEdit, QMenu, QStackedWidget, QToolButton, QTreeWidget,
QTreeWidgetItem, QVBoxLayout, QWidget QTreeWidgetItem, QVBoxLayout, QWidget
) )
@@ -40,7 +40,7 @@ from novelwriter.constants import nwLabels, trConst
from novelwriter.core.status import NWStatus, StatusEntry from novelwriter.core.status import NWStatus, StatusEntry
from novelwriter.enum import nwStatusShape from novelwriter.enum import nwStatusShape
from novelwriter.extensions.configlayout import NColourLabel, NFixedPage, NScrollableForm from novelwriter.extensions.configlayout import NColourLabel, NFixedPage, NScrollableForm
from novelwriter.extensions.modified import NComboBox, NIconToolButton from novelwriter.extensions.modified import NComboBox, NDialog, NIconToolButton
from novelwriter.extensions.pagedsidebar import NPagedSideBar from novelwriter.extensions.pagedsidebar import NPagedSideBar
from novelwriter.extensions.switch import NSwitch from novelwriter.extensions.switch import NSwitch
from novelwriter.types import ( from novelwriter.types import (
@@ -51,7 +51,7 @@ from novelwriter.types import (
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class GuiProjectSettings(QDialog): class GuiProjectSettings(NDialog):
PAGE_SETTINGS = 0 PAGE_SETTINGS = 0
PAGE_STATUS = 1 PAGE_STATUS = 1
@@ -147,7 +147,7 @@ class GuiProjectSettings(QDialog):
"""Capture the user closing the window and save settings.""" """Capture the user closing the window and save settings."""
self._saveSettings() self._saveSettings()
event.accept() event.accept()
self.deleteLater() self.softDelete()
return return
## ##
+9 -5
View File
@@ -28,18 +28,22 @@ import logging
from PyQt5.QtCore import QSize, pyqtSlot from PyQt5.QtCore import QSize, pyqtSlot
from PyQt5.QtGui import QFontMetrics from PyQt5.QtGui import QFontMetrics
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
QDialog, QDialogButtonBox, QFrame, QHBoxLayout, QLabel, QListWidget, QDialogButtonBox, QFrame, QHBoxLayout, QLabel, QListWidget,
QListWidgetItem, QVBoxLayout, QWidget QListWidgetItem, QVBoxLayout, QWidget
) )
from novelwriter import CONFIG from novelwriter import CONFIG
from novelwriter.constants import nwQuotes, trConst from novelwriter.constants import nwQuotes, trConst
from novelwriter.types import QtAlignCenter, QtAlignTop, QtDialogCancel, QtDialogOk, QtUserRole from novelwriter.extensions.modified import NDialog
from novelwriter.types import (
QtAccepted, QtAlignCenter, QtAlignTop, QtDialogCancel, QtDialogOk,
QtUserRole
)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class GuiQuoteSelect(QDialog): class GuiQuoteSelect(NDialog):
_selected = "" _selected = ""
@@ -126,8 +130,8 @@ class GuiQuoteSelect(QDialog):
cls = GuiQuoteSelect(parent, current=current) cls = GuiQuoteSelect(parent, current=current)
cls.exec() cls.exec()
quote = cls._selected quote = cls._selected
accepted = cls.result() == QDialog.DialogCode.Accepted accepted = cls.result() == QtAccepted
cls.deleteLater() cls.softDelete()
return quote, accepted return quote, accepted
## ##
+4 -4
View File
@@ -30,7 +30,7 @@ from pathlib import Path
from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot
from PyQt5.QtGui import QCloseEvent from PyQt5.QtGui import QCloseEvent
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
QAbstractItemView, QApplication, QDialog, QDialogButtonBox, QFileDialog, QAbstractItemView, QApplication, QDialogButtonBox, QFileDialog,
QHBoxLayout, QLineEdit, QListWidget, QVBoxLayout, QWidget QHBoxLayout, QLineEdit, QListWidget, QVBoxLayout, QWidget
) )
@@ -38,13 +38,13 @@ from novelwriter import CONFIG, SHARED
from novelwriter.common import formatFileFilter from novelwriter.common import formatFileFilter
from novelwriter.core.spellcheck import UserDictionary from novelwriter.core.spellcheck import UserDictionary
from novelwriter.extensions.configlayout import NColourLabel from novelwriter.extensions.configlayout import NColourLabel
from novelwriter.extensions.modified import NIconToolButton from novelwriter.extensions.modified import NDialog, NIconToolButton
from novelwriter.types import QtDialogClose, QtDialogSave from novelwriter.types import QtDialogClose, QtDialogSave
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class GuiWordList(QDialog): class GuiWordList(NDialog):
newWordListReady = pyqtSignal() newWordListReady = pyqtSignal()
@@ -140,7 +140,7 @@ class GuiWordList(QDialog):
"""Capture the close event and perform cleanup.""" """Capture the close event and perform cleanup."""
self._saveGuiSettings() self._saveGuiSettings()
event.accept() event.accept()
self.deleteLater() self.softDelete()
return return
## ##
+22 -3
View File
@@ -31,7 +31,7 @@ from enum import Enum
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from PyQt5.QtCore import QSize, Qt from PyQt5.QtCore import QSize, Qt
from PyQt5.QtGui import QWheelEvent from PyQt5.QtGui import QKeyEvent, QKeySequence, QWheelEvent
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
QApplication, QComboBox, QDialog, QDoubleSpinBox, QSpinBox, QToolButton, QApplication, QComboBox, QDialog, QDoubleSpinBox, QSpinBox, QToolButton,
QWidget QWidget
@@ -43,7 +43,26 @@ if TYPE_CHECKING: # pragma: no cover
from novelwriter.guimain import GuiMain from novelwriter.guimain import GuiMain
class NToolDialog(QDialog): class NDialog(QDialog):
def softDelete(self) -> None:
"""Since calling deleteLater is sometimes not safe from Python,
as the C++ object can be deleted before the Python process is
done with the object, we instead set the dialog's parent to None
so that it gets garbage collected when it runs out of scope.
"""
self.setParent(None) # type: ignore
return
def keyPressEvent(self, event: QKeyEvent) -> None:
"""Overload keyPressEvent and forward escape to close."""
if event.matches(QKeySequence.StandardKey.Cancel):
self.close()
event.ignore()
return
class NToolDialog(NDialog):
def __init__(self, parent: GuiMain) -> None: def __init__(self, parent: GuiMain) -> None:
super().__init__(parent=parent) super().__init__(parent=parent)
@@ -62,7 +81,7 @@ class NToolDialog(QDialog):
return return
class NNonBlockingDialog(QDialog): class NNonBlockingDialog(NDialog):
def __init__(self, parent: QWidget | None = None) -> None: def __init__(self, parent: QWidget | None = None) -> None:
super().__init__(parent=parent) super().__init__(parent=parent)
+16 -26
View File
@@ -34,9 +34,8 @@ from time import time
from PyQt5.QtCore import QPoint, Qt, QTimer, pyqtSignal, pyqtSlot from PyQt5.QtCore import QPoint, Qt, QTimer, pyqtSignal, pyqtSlot
from PyQt5.QtGui import QDragEnterEvent, QDragMoveEvent, QDropEvent, QIcon, QMouseEvent, QPalette from PyQt5.QtGui import QDragEnterEvent, QDragMoveEvent, QDropEvent, QIcon, QMouseEvent, QPalette
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
QAbstractItemView, QAction, QDialog, QFrame, QHBoxLayout, QHeaderView, QAbstractItemView, QAction, QFrame, QHBoxLayout, QHeaderView, QLabel,
QLabel, QMenu, QShortcut, QTreeWidget, QTreeWidgetItem, QVBoxLayout, QMenu, QShortcut, QTreeWidget, QTreeWidgetItem, QVBoxLayout, QWidget
QWidget
) )
from novelwriter import CONFIG, SHARED from novelwriter import CONFIG, SHARED
@@ -1397,14 +1396,10 @@ class GuiProjectTree(QTreeWidget):
if not newFile: if not newFile:
itemList.remove(tHandle) itemList.remove(tHandle)
dlgMerge = GuiDocMerge(SHARED.mainGui, tHandle, itemList) data, status = GuiDocMerge.getData(SHARED.mainGui, tHandle, itemList)
dlgMerge.exec() if status:
items = data.get("finalItems", [])
if dlgMerge.result() == QDialog.DialogCode.Accepted: if not items:
mrgData = dlgMerge.getData()
mrgList = mrgData.get("finalItems", [])
if not mrgList:
SHARED.info(self.tr("No documents selected for merging.")) SHARED.info(self.tr("No documents selected for merging."))
return False return False
@@ -1424,7 +1419,7 @@ class GuiProjectTree(QTreeWidget):
else: else:
return False return False
for sHandle in mrgList: for sHandle in items:
docMerger.appendText(sHandle, True, mLabel) docMerger.appendText(sHandle, True, mLabel)
if not docMerger.writeTargetDoc(): if not docMerger.writeTargetDoc():
@@ -1441,8 +1436,8 @@ class GuiProjectTree(QTreeWidget):
self.projView.openDocumentRequest.emit(mHandle, nwDocMode.EDIT, "", False) self.projView.openDocumentRequest.emit(mHandle, nwDocMode.EDIT, "", False)
self.projView.setSelectedHandle(mHandle, doScroll=True) self.projView.setSelectedHandle(mHandle, doScroll=True)
if mrgData.get("moveToTrash", False): if data.get("moveToTrash", False):
for sHandle in reversed(mrgData.get("finalItems", [])): for sHandle in reversed(data.get("finalItems", [])):
trItem = self._getTreeItem(sHandle) trItem = self._getTreeItem(sHandle)
if isinstance(trItem, QTreeWidgetItem) and trItem.childCount() == 0: if isinstance(trItem, QTreeWidgetItem) and trItem.childCount() == 0:
self.moveItemToTrash(sHandle, askFirst=False, flush=False) self.moveItemToTrash(sHandle, askFirst=False, flush=False)
@@ -1468,16 +1463,11 @@ class GuiProjectTree(QTreeWidget):
logger.error("Only valid document items can be split") logger.error("Only valid document items can be split")
return False return False
dlgSplit = GuiDocSplit(SHARED.mainGui, tHandle) data, text, status = GuiDocSplit.getData(SHARED.mainGui, tHandle)
dlgSplit.exec() if status:
headerList = data.get("headerList", [])
if dlgSplit.result() == QDialog.DialogCode.Accepted: intoFolder = data.get("intoFolder", False)
docHierarchy = data.get("docHierarchy", False)
splitData, splitText = dlgSplit.getData()
headerList = splitData.get("headerList", [])
intoFolder = splitData.get("intoFolder", False)
docHierarchy = splitData.get("docHierarchy", False)
docSplit = DocSplitter(SHARED.project, tHandle) docSplit = DocSplitter(SHARED.project, tHandle)
if intoFolder: if intoFolder:
@@ -1487,7 +1477,7 @@ class GuiProjectTree(QTreeWidget):
else: else:
docSplit.setParentItem(tItem.itemParent) docSplit.setParentItem(tItem.itemParent)
docSplit.splitDocument(headerList, splitText) docSplit.splitDocument(headerList, text)
for writeOk, dHandle, nHandle in docSplit.writeDocuments(docHierarchy): for writeOk, dHandle, nHandle in docSplit.writeDocuments(docHierarchy):
SHARED.project.index.reIndexHandle(dHandle) SHARED.project.index.reIndexHandle(dHandle)
self.revealNewTreeItem(dHandle, nHandle=nHandle, wordCount=True) self.revealNewTreeItem(dHandle, nHandle=nHandle, wordCount=True)
@@ -1498,7 +1488,7 @@ class GuiProjectTree(QTreeWidget):
info=docSplit.getError() info=docSplit.getError()
) )
if splitData.get("moveToTrash", False): if data.get("moveToTrash", False):
self.moveItemToTrash(tHandle, askFirst=False, flush=True) self.moveItemToTrash(tHandle, askFirst=False, flush=True)
self.saveTreeOrder() self.saveTreeOrder()
+1 -2
View File
@@ -827,8 +827,7 @@ class GuiMain(QMainWindow):
def showAboutNWDialog(self) -> None: def showAboutNWDialog(self) -> None:
"""Show the novelWriter about dialog.""" """Show the novelWriter about dialog."""
dialog = GuiAbout(self) dialog = GuiAbout(self)
dialog.activateDialog() dialog.exec()
dialog.populateGUI()
return return
@pyqtSlot() @pyqtSlot()
-4
View File
@@ -316,7 +316,6 @@ class SharedData(QObject):
if log: if log:
logger.info(self._lastAlert, stacklevel=2) logger.info(self._lastAlert, stacklevel=2)
alert.exec() alert.exec()
alert.deleteLater()
return return
def warn(self, text: str, info: str = "", details: str = "", log: bool = True) -> None: def warn(self, text: str, info: str = "", details: str = "", log: bool = True) -> None:
@@ -328,7 +327,6 @@ class SharedData(QObject):
if log: if log:
logger.warning(self._lastAlert, stacklevel=2) logger.warning(self._lastAlert, stacklevel=2)
alert.exec() alert.exec()
alert.deleteLater()
return return
def error(self, text: str, info: str = "", details: str = "", log: bool = True, def error(self, text: str, info: str = "", details: str = "", log: bool = True,
@@ -343,7 +341,6 @@ class SharedData(QObject):
if log: if log:
logger.error(self._lastAlert, stacklevel=2) logger.error(self._lastAlert, stacklevel=2)
alert.exec() alert.exec()
alert.deleteLater()
return return
def question(self, text: str, info: str = "", details: str = "", warn: bool = False) -> bool: def question(self, text: str, info: str = "", details: str = "", warn: bool = False) -> bool:
@@ -354,7 +351,6 @@ class SharedData(QObject):
self._lastAlert = alert.logMessage self._lastAlert = alert.logMessage
alert.exec() alert.exec()
isYes = alert.result() == QMessageBox.StandardButton.Yes isYes = alert.result() == QMessageBox.StandardButton.Yes
alert.deleteLater()
return isYes return isYes
## ##
+1 -1
View File
@@ -172,7 +172,7 @@ class GuiDictionaries(NNonBlockingDialog):
def closeEvent(self, event: QCloseEvent) -> None: def closeEvent(self, event: QCloseEvent) -> None:
"""Capture the user closing the window.""" """Capture the user closing the window."""
event.accept() event.accept()
self.deleteLater() self.softDelete()
return return
## ##
+5 -4
View File
@@ -28,19 +28,20 @@ import random
from PyQt5.QtCore import Qt, pyqtSlot from PyQt5.QtCore import Qt, pyqtSlot
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
QDialog, QDialogButtonBox, QGridLayout, QHBoxLayout, QLabel, QSpinBox, QDialogButtonBox, QGridLayout, QHBoxLayout, QLabel, QSpinBox, QVBoxLayout,
QVBoxLayout, QWidget QWidget
) )
from novelwriter import CONFIG, SHARED from novelwriter import CONFIG, SHARED
from novelwriter.common import readTextFile from novelwriter.common import readTextFile
from novelwriter.extensions.modified import NDialog
from novelwriter.extensions.switch import NSwitch from novelwriter.extensions.switch import NSwitch
from novelwriter.types import QtAlignLeft, QtAlignRight, QtDialogClose, QtRoleAction from novelwriter.types import QtAlignLeft, QtAlignRight, QtDialogClose, QtRoleAction
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class GuiLipsum(QDialog): class GuiLipsum(NDialog):
def __init__(self, parent: QWidget) -> None: def __init__(self, parent: QWidget) -> None:
super().__init__(parent=parent) super().__init__(parent=parent)
@@ -132,7 +133,7 @@ class GuiLipsum(QDialog):
cls = GuiLipsum(parent) cls = GuiLipsum(parent)
cls.exec() cls.exec()
text = cls.lipsumText text = cls.lipsumText
cls.deleteLater() cls.softDelete()
return text return text
## ##
+4 -4
View File
@@ -30,7 +30,7 @@ from pathlib import Path
from PyQt5.QtCore import QTimer, pyqtSlot from PyQt5.QtCore import QTimer, pyqtSlot
from PyQt5.QtGui import QCloseEvent from PyQt5.QtGui import QCloseEvent
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
QAbstractButton, QAbstractItemView, QDialog, QDialogButtonBox, QFileDialog, QAbstractButton, QAbstractItemView, QDialogButtonBox, QFileDialog,
QGridLayout, QHBoxLayout, QLabel, QLineEdit, QListWidget, QListWidgetItem, QGridLayout, QHBoxLayout, QLabel, QLineEdit, QListWidget, QListWidgetItem,
QPushButton, QSplitter, QVBoxLayout, QWidget QPushButton, QSplitter, QVBoxLayout, QWidget
) )
@@ -42,14 +42,14 @@ from novelwriter.core.buildsettings import BuildSettings
from novelwriter.core.docbuild import NWBuildDocument from novelwriter.core.docbuild import NWBuildDocument
from novelwriter.core.item import NWItem from novelwriter.core.item import NWItem
from novelwriter.enum import nwBuildFmt from novelwriter.enum import nwBuildFmt
from novelwriter.extensions.modified import NIconToolButton from novelwriter.extensions.modified import NDialog, NIconToolButton
from novelwriter.extensions.simpleprogress import NProgressSimple from novelwriter.extensions.simpleprogress import NProgressSimple
from novelwriter.types import QtAlignCenter, QtDialogClose, QtRoleAction, QtRoleReject, QtUserRole from novelwriter.types import QtAlignCenter, QtDialogClose, QtRoleAction, QtRoleReject, QtUserRole
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class GuiManuscriptBuild(QDialog): class GuiManuscriptBuild(NDialog):
"""GUI Tools: Manuscript Build Dialog """GUI Tools: Manuscript Build Dialog
This is the tool for running the build itself. It can be accessed This is the tool for running the build itself. It can be accessed
@@ -250,7 +250,7 @@ class GuiManuscriptBuild(QDialog):
""" """
self._saveSettings() self._saveSettings()
event.accept() event.accept()
self.deleteLater() self.softDelete()
return return
## ##
+2 -3
View File
@@ -265,7 +265,7 @@ class GuiManuscript(NToolDialog):
if isinstance(obj, GuiBuildSettings) and obj.isVisible(): if isinstance(obj, GuiBuildSettings) and obj.isVisible():
obj.close() obj.close()
event.accept() event.accept()
self.deleteLater() self.softDelete()
return return
## ##
@@ -891,9 +891,8 @@ class _PreviewWidget(QTextBrowser):
document within the viewport. document within the viewport.
""" """
vBar = self.verticalScrollBar() vBar = self.verticalScrollBar()
sW = vBar.width() if vBar.isVisible() else 0
tB = self.frameWidth() tB = self.frameWidth()
vW = self.width() - 2*tB - sW vW = self.width() - 2*tB - vBar.width()
vH = self.height() - 2*tB vH = self.height() - 2*tB
tH = self.ageLabel.height() tH = self.ageLabel.height()
pS = self.buildProgress.width() pS = self.buildProgress.width()
+1 -1
View File
@@ -200,7 +200,7 @@ class GuiBuildSettings(NToolDialog):
self._askToSaveBuild() self._askToSaveBuild()
self._saveSettings() self._saveSettings()
event.accept() event.accept()
self.deleteLater() self.softDelete()
return return
## ##
+1 -1
View File
@@ -150,7 +150,7 @@ class GuiNovelDetails(NNonBlockingDialog):
"""Capture the user closing the window and save settings.""" """Capture the user closing the window and save settings."""
self._saveSettings() self._saveSettings()
event.accept() event.accept()
self.deleteLater() self.softDelete()
return return
## ##
+5 -5
View File
@@ -34,8 +34,8 @@ from PyQt5.QtCore import (
) )
from PyQt5.QtGui import QCloseEvent, QColor, QFont, QPainter, QPaintEvent, QPen from PyQt5.QtGui import QCloseEvent, QColor, QFont, QPainter, QPaintEvent, QPen
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
QAction, QApplication, QDialog, QFileDialog, QFormLayout, QHBoxLayout, QAction, QApplication, QFileDialog, QFormLayout, QHBoxLayout, QLabel,
QLabel, QLineEdit, QListView, QMenu, QPushButton, QScrollArea, QShortcut, QLineEdit, QListView, QMenu, QPushButton, QScrollArea, QShortcut,
QStackedWidget, QStyledItemDelegate, QStyleOptionViewItem, QVBoxLayout, QStackedWidget, QStyledItemDelegate, QStyleOptionViewItem, QVBoxLayout,
QWidget QWidget
) )
@@ -46,7 +46,7 @@ from novelwriter.constants import nwFiles
from novelwriter.core.coretools import ProjectBuilder from novelwriter.core.coretools import ProjectBuilder
from novelwriter.enum import nwItemClass from novelwriter.enum import nwItemClass
from novelwriter.extensions.configlayout import NWrappedWidgetBox from novelwriter.extensions.configlayout import NWrappedWidgetBox
from novelwriter.extensions.modified import NIconToolButton, NSpinBox from novelwriter.extensions.modified import NDialog, NIconToolButton, NSpinBox
from novelwriter.extensions.switch import NSwitch from novelwriter.extensions.switch import NSwitch
from novelwriter.extensions.versioninfo import VersionInfoWidget from novelwriter.extensions.versioninfo import VersionInfoWidget
from novelwriter.types import QtAlignLeft, QtAlignRightTop, QtSelected from novelwriter.types import QtAlignLeft, QtAlignRightTop, QtSelected
@@ -56,7 +56,7 @@ logger = logging.getLogger(__name__)
PANEL_ALPHA = 178 PANEL_ALPHA = 178
class GuiWelcome(QDialog): class GuiWelcome(NDialog):
openProjectRequest = pyqtSignal(Path) openProjectRequest = pyqtSignal(Path)
@@ -196,7 +196,7 @@ class GuiWelcome(QDialog):
"""Capture the user closing the window and save settings.""" """Capture the user closing the window and save settings."""
self._saveSettings() self._saveSettings()
event.accept() event.accept()
self.deleteLater() self.softDelete()
return return
## ##
+1 -1
View File
@@ -318,7 +318,7 @@ class GuiWritingStats(NToolDialog):
def closeEvent(self, event: QCloseEvent) -> None: def closeEvent(self, event: QCloseEvent) -> None:
"""Capture the user closing the window.""" """Capture the user closing the window."""
event.accept() event.accept()
self.deleteLater() self.softDelete()
return return
## ##
+4 -1
View File
@@ -25,7 +25,7 @@ from __future__ import annotations
from PyQt5.QtCore import QRegularExpression, Qt from PyQt5.QtCore import QRegularExpression, Qt
from PyQt5.QtGui import QColor, QFont, QPainter, QTextCharFormat, QTextCursor, QTextFormat from PyQt5.QtGui import QColor, QFont, QPainter, QTextCharFormat, QTextCursor, QTextFormat
from PyQt5.QtWidgets import QDialogButtonBox, QSizePolicy, QStyle from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QSizePolicy, QStyle
# Qt Alignment Flags # Qt Alignment Flags
@@ -80,6 +80,9 @@ QtMouseMiddle = Qt.MouseButton.MiddleButton
# Dialog Button Box Types # Dialog Button Box Types
QtAccepted = QDialog.DialogCode.Accepted
QtRejected = QDialog.DialogCode.Rejected
QtDialogApply = QDialogButtonBox.StandardButton.Apply QtDialogApply = QDialogButtonBox.StandardButton.Apply
QtDialogCancel = QDialogButtonBox.StandardButton.Cancel QtDialogCancel = QDialogButtonBox.StandardButton.Cancel
QtDialogClose = QDialogButtonBox.StandardButton.Close QtDialogClose = QDialogButtonBox.StandardButton.Close
+2 -2
View File
@@ -33,9 +33,9 @@ from novelwriter.dialogs.about import GuiAbout
@pytest.mark.gui @pytest.mark.gui
def testDlgAbout_NWDialog(qtbot, monkeypatch, nwGUI): def testDlgAbout_NWDialog(qtbot, monkeypatch, nwGUI):
"""Test the novelWriter about dialogs.""" """Test the novelWriter about dialogs."""
# NW About monkeypatch.setattr(GuiAbout, "exec", lambda *a: None)
nwGUI.showAboutNWDialog()
nwGUI.showAboutNWDialog()
qtbot.waitUntil(lambda: SHARED.findTopLevelWidget(GuiAbout) is not None, timeout=1000) qtbot.waitUntil(lambda: SHARED.findTopLevelWidget(GuiAbout) is not None, timeout=1000)
msgAbout = SHARED.findTopLevelWidget(GuiAbout) msgAbout = SHARED.findTopLevelWidget(GuiAbout)
assert isinstance(msgAbout, GuiAbout) assert isinstance(msgAbout, GuiAbout)
+7 -6
View File
@@ -23,10 +23,11 @@ from __future__ import annotations
import pytest import pytest
from PyQt5.QtCore import QItemSelectionModel from PyQt5.QtCore import QItemSelectionModel
from PyQt5.QtWidgets import QDialog, QListWidgetItem from PyQt5.QtWidgets import QListWidgetItem
from novelwriter.dialogs.editlabel import GuiEditLabel from novelwriter.dialogs.editlabel import GuiEditLabel
from novelwriter.dialogs.quotes import GuiQuoteSelect from novelwriter.dialogs.quotes import GuiQuoteSelect
from novelwriter.types import QtAccepted, QtRejected
@pytest.mark.gui @pytest.mark.gui
@@ -47,17 +48,17 @@ def testDlgOther_QuoteSelect(qtbot, monkeypatch, nwGUI):
assert nwQuot.previewLabel.text() == lastItem assert nwQuot.previewLabel.text() == lastItem
nwQuot.accept() nwQuot.accept()
assert nwQuot.result() == QDialog.DialogCode.Accepted assert nwQuot.result() == QtAccepted
assert nwQuot.selectedQuote == lastItem assert nwQuot.selectedQuote == lastItem
nwQuot.close() nwQuot.close()
# Test Class Method # Test Class Method
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr(GuiQuoteSelect, "result", lambda *a: QDialog.DialogCode.Accepted) mp.setattr(GuiQuoteSelect, "result", lambda *a: QtAccepted)
assert GuiQuoteSelect.getQuote(nwGUI, current="X") == ("X", True) assert GuiQuoteSelect.getQuote(nwGUI, current="X") == ("X", True)
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr(GuiQuoteSelect, "result", lambda *a: QDialog.DialogCode.Rejected) mp.setattr(GuiQuoteSelect, "result", lambda *a: QtRejected)
assert GuiQuoteSelect.getQuote(nwGUI, current="X") == ("X", False) assert GuiQuoteSelect.getQuote(nwGUI, current="X") == ("X", False)
# qtbot.stop() # qtbot.stop()
@@ -69,13 +70,13 @@ def testDlgOther_EditLabel(qtbot, monkeypatch):
monkeypatch.setattr(GuiEditLabel, "exec", lambda *a: None) monkeypatch.setattr(GuiEditLabel, "exec", lambda *a: None)
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr(GuiEditLabel, "result", lambda *a: QDialog.DialogCode.Accepted) mp.setattr(GuiEditLabel, "result", lambda *a: QtAccepted)
newLabel, dlgOk = GuiEditLabel.getLabel(None, text="Hello World") # type: ignore newLabel, dlgOk = GuiEditLabel.getLabel(None, text="Hello World") # type: ignore
assert dlgOk is True assert dlgOk is True
assert newLabel == "Hello World" assert newLabel == "Hello World"
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr(GuiEditLabel, "result", lambda *a: QDialog.DialogCode.Rejected) mp.setattr(GuiEditLabel, "result", lambda *a: QtRejected)
newLabel, dlgOk = GuiEditLabel.getLabel(None, text="Hello World") # type: ignore newLabel, dlgOk = GuiEditLabel.getLabel(None, text="Hello World") # type: ignore
assert dlgOk is False assert dlgOk is False
assert newLabel == "Hello World" assert newLabel == "Hello World"
+27 -8
View File
@@ -25,16 +25,15 @@ import pytest
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt
from novelwriter.dialogs.docmerge import GuiDocMerge from novelwriter.dialogs.docmerge import GuiDocMerge
from novelwriter.types import QtUserRole from novelwriter.types import QtAccepted, QtRejected, QtUserRole
from tests.tools import C, buildTestProject from tests.tools import C, buildTestProject
@pytest.mark.gui @pytest.mark.gui
def testDlgMerge_Main(qtbot, nwGUI, projPath, mockRnd): def testDlgMerge_Main(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
"""Test the merge documents tool. """Test the merge documents tool."""
""" monkeypatch.setattr(GuiDocMerge, "exec", lambda *a: None)
# Create a new project
buildTestProject(nwGUI, projPath) buildTestProject(nwGUI, projPath)
# Check that the dialog kan handle invalid items # Check that the dialog kan handle invalid items
@@ -54,13 +53,16 @@ def testDlgMerge_Main(qtbot, nwGUI, projPath, mockRnd):
itemOne = nwMerge.listBox.item(0) itemOne = nwMerge.listBox.item(0)
itemTwo = nwMerge.listBox.item(1) itemTwo = nwMerge.listBox.item(1)
assert itemOne is not None
assert itemTwo is not None
assert itemOne.data(QtUserRole) == C.hChapterDoc assert itemOne.data(QtUserRole) == C.hChapterDoc
assert itemTwo.data(QtUserRole) == C.hSceneDoc assert itemTwo.data(QtUserRole) == C.hSceneDoc
assert itemOne.checkState() == Qt.CheckState.Checked assert itemOne.checkState() == Qt.CheckState.Checked
assert itemTwo.checkState() == Qt.CheckState.Checked assert itemTwo.checkState() == Qt.CheckState.Checked
data = nwMerge.getData() data = nwMerge.data()
assert data["sHandle"] == C.hChapterDir assert data["sHandle"] == C.hChapterDir
assert data["origItems"] == [C.hChapterDir, C.hChapterDoc, C.hSceneDoc] assert data["origItems"] == [C.hChapterDir, C.hChapterDoc, C.hSceneDoc]
assert data["moveToTrash"] is False assert data["moveToTrash"] is False
@@ -70,7 +72,7 @@ def testDlgMerge_Main(qtbot, nwGUI, projPath, mockRnd):
itemTwo.setCheckState(Qt.CheckState.Unchecked) itemTwo.setCheckState(Qt.CheckState.Unchecked)
nwMerge.trashSwitch.setChecked(True) nwMerge.trashSwitch.setChecked(True)
data = nwMerge.getData() data = nwMerge.data()
assert data["sHandle"] == C.hChapterDir assert data["sHandle"] == C.hChapterDir
assert data["origItems"] == [C.hChapterDir, C.hChapterDoc, C.hSceneDoc] assert data["origItems"] == [C.hChapterDir, C.hChapterDoc, C.hSceneDoc]
assert data["moveToTrash"] is True assert data["moveToTrash"] is True
@@ -79,10 +81,27 @@ def testDlgMerge_Main(qtbot, nwGUI, projPath, mockRnd):
# Restore default values # Restore default values
nwMerge._resetList() nwMerge._resetList()
data = nwMerge.getData() data = nwMerge.data()
assert data["sHandle"] == C.hChapterDir assert data["sHandle"] == C.hChapterDir
assert data["origItems"] == [C.hChapterDir, C.hChapterDoc, C.hSceneDoc] assert data["origItems"] == [C.hChapterDir, C.hChapterDoc, C.hSceneDoc]
assert data["moveToTrash"] is True assert data["moveToTrash"] is True
assert data["finalItems"] == [C.hChapterDoc, C.hSceneDoc] assert data["finalItems"] == [C.hChapterDoc, C.hSceneDoc]
# Test Class Method
with monkeypatch.context() as mp:
mp.setattr(GuiDocMerge, "result", lambda *a: QtAccepted)
data, status = GuiDocMerge.getData(
nwGUI, C.hChapterDir, [C.hChapterDir, C.hChapterDoc, C.hSceneDoc]
)
assert data["sHandle"] == C.hChapterDir
assert status is True
with monkeypatch.context() as mp:
mp.setattr(GuiDocMerge, "result", lambda *a: QtRejected)
data, status = GuiDocMerge.getData(
nwGUI, C.hChapterDir, [C.hChapterDir, C.hChapterDoc, C.hSceneDoc]
)
assert data["sHandle"] == C.hChapterDir
assert status is False
# qtbot.stop() # qtbot.stop()
+18 -4
View File
@@ -25,6 +25,7 @@ import pytest
from novelwriter import SHARED from novelwriter import SHARED
from novelwriter.dialogs.docsplit import GuiDocSplit from novelwriter.dialogs.docsplit import GuiDocSplit
from novelwriter.dialogs.editlabel import GuiEditLabel from novelwriter.dialogs.editlabel import GuiEditLabel
from novelwriter.types import QtAccepted, QtRejected
from tests.tools import C, buildTestProject from tests.tools import C, buildTestProject
@@ -33,8 +34,7 @@ from tests.tools import C, buildTestProject
def testDlgSplit_Main(qtbot, monkeypatch, nwGUI, projPath, mockRnd): def testDlgSplit_Main(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
"""Test the split document tool.""" """Test the split document tool."""
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True)) monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
monkeypatch.setattr(GuiDocSplit, "exec", lambda *a: None)
# Create a new project
buildTestProject(nwGUI, projPath) buildTestProject(nwGUI, projPath)
project = SHARED.project project = SHARED.project
@@ -74,7 +74,7 @@ def testDlgSplit_Main(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
nwSplit.splitLevel.setCurrentIndex(3) nwSplit.splitLevel.setCurrentIndex(3)
assert nwSplit.listBox.count() == 12 assert nwSplit.listBox.count() == 12
data, text = nwSplit.getData() data, text = nwSplit.data()
assert text == docText.splitlines() assert text == docText.splitlines()
assert data["sHandle"] == hSplitDoc assert data["sHandle"] == hSplitDoc
assert data["spLevel"] == 4 assert data["spLevel"] == 4
@@ -97,5 +97,19 @@ def testDlgSplit_Main(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
nwSplit._loadContent(C.hNovelRoot) nwSplit._loadContent(C.hNovelRoot)
assert nwSplit.listBox.count() == 0 assert nwSplit.listBox.count() == 0
nwSplit.reject() # Test Class Method
with monkeypatch.context() as mp:
mp.setattr(GuiDocSplit, "result", lambda *a: QtAccepted)
data, text, status = GuiDocSplit.getData(nwGUI, hSplitDoc)
assert data["sHandle"] == hSplitDoc
assert text == docText.splitlines()
assert status is True
with monkeypatch.context() as mp:
mp.setattr(GuiDocSplit, "result", lambda *a: QtRejected)
data, text, status = GuiDocSplit.getData(nwGUI, hSplitDoc)
assert data["sHandle"] == hSplitDoc
assert text == docText.splitlines()
assert status is False
# qtbot.stop() # qtbot.stop()
@@ -91,7 +91,6 @@ def testDlgPreferences_Main(qtbot, monkeypatch, nwGUI, tstPaths):
def testDlgPreferences_Actions(qtbot, monkeypatch, nwGUI): def testDlgPreferences_Actions(qtbot, monkeypatch, nwGUI):
"""Test the preferences dialog actions.""" """Test the preferences dialog actions."""
monkeypatch.setattr(SHARED._spelling, "listDictionaries", lambda: [("en", "English [en]")]) monkeypatch.setattr(SHARED._spelling, "listDictionaries", lambda: [("en", "English [en]")])
monkeypatch.setattr(GuiPreferences, "deleteLater", lambda *a: None)
prefs = GuiPreferences(nwGUI) prefs = GuiPreferences(nwGUI)
prefs.show() prefs.show()
@@ -155,7 +154,6 @@ def testDlgPreferences_Settings(qtbot, monkeypatch, nwGUI, tstPaths):
monkeypatch.setattr(SHARED._spelling, "listDictionaries", lambda: spelling) monkeypatch.setattr(SHARED._spelling, "listDictionaries", lambda: spelling)
monkeypatch.setattr(CONFIG, "listLanguages", lambda *a: languages) monkeypatch.setattr(CONFIG, "listLanguages", lambda *a: languages)
monkeypatch.setattr(GuiPreferences, "deleteLater", lambda *a: None)
prefs = GuiPreferences(nwGUI) prefs = GuiPreferences(nwGUI)
prefs.show() prefs.show()
@@ -23,13 +23,13 @@ from __future__ import annotations
import pytest import pytest
from PyQt5.QtGui import QColor from PyQt5.QtGui import QColor
from PyQt5.QtWidgets import QAction, QColorDialog, QDialog from PyQt5.QtWidgets import QAction, QColorDialog
from novelwriter import CONFIG, SHARED from novelwriter import CONFIG, SHARED
from novelwriter.dialogs.editlabel import GuiEditLabel from novelwriter.dialogs.editlabel import GuiEditLabel
from novelwriter.dialogs.projectsettings import GuiProjectSettings from novelwriter.dialogs.projectsettings import GuiProjectSettings
from novelwriter.enum import nwItemType, nwStatusShape from novelwriter.enum import nwItemType, nwStatusShape
from novelwriter.types import QtMouseLeft from novelwriter.types import QtAccepted, QtMouseLeft
from tests.tools import C, buildTestProject from tests.tools import C, buildTestProject
@@ -43,7 +43,7 @@ def testDlgProjSettings_Dialog(qtbot, monkeypatch, nwGUI):
""" """
# Block the GUI blocking thread # Block the GUI blocking thread
monkeypatch.setattr(GuiProjectSettings, "exec", lambda *a: None) monkeypatch.setattr(GuiProjectSettings, "exec", lambda *a: None)
monkeypatch.setattr(GuiProjectSettings, "result", lambda *a: QDialog.DialogCode.Accepted) monkeypatch.setattr(GuiProjectSettings, "result", lambda *a: QtAccepted)
# Check that we cannot open when there is no project # Check that we cannot open when there is no project
nwGUI.mainMenu.aProjectSettings.activate(QAction.Trigger) nwGUI.mainMenu.aProjectSettings.activate(QAction.Trigger)
+3 -2
View File
@@ -23,11 +23,12 @@ from __future__ import annotations
import pytest import pytest
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QAction, QDialog, QFileDialog from PyQt5.QtWidgets import QAction, QFileDialog
from novelwriter import SHARED from novelwriter import SHARED
from novelwriter.core.spellcheck import UserDictionary from novelwriter.core.spellcheck import UserDictionary
from novelwriter.dialogs.wordlist import GuiWordList from novelwriter.dialogs.wordlist import GuiWordList
from novelwriter.types import QtAccepted
from tests.mocked import causeOSError from tests.mocked import causeOSError
from tests.tools import buildTestProject from tests.tools import buildTestProject
@@ -39,7 +40,7 @@ def testDlgWordList_Dialog(qtbot, monkeypatch, nwGUI, fncPath, projPath):
buildTestProject(nwGUI, projPath) buildTestProject(nwGUI, projPath)
monkeypatch.setattr(GuiWordList, "exec", lambda *a: None) monkeypatch.setattr(GuiWordList, "exec", lambda *a: None)
monkeypatch.setattr(GuiWordList, "result", lambda *a: QDialog.DialogCode.Accepted) monkeypatch.setattr(GuiWordList, "result", lambda *a: QtAccepted)
monkeypatch.setattr(GuiWordList, "accept", lambda *a: None) monkeypatch.setattr(GuiWordList, "accept", lambda *a: None)
# Open project # Open project
+10 -8
View File
@@ -26,7 +26,7 @@ import pytest
from PyQt5.QtCore import QEvent, QMimeData, QPoint, Qt, QTimer from PyQt5.QtCore import QEvent, QMimeData, QPoint, Qt, QTimer
from PyQt5.QtGui import QDragEnterEvent, QDragMoveEvent, QDropEvent, QMouseEvent from PyQt5.QtGui import QDragEnterEvent, QDragMoveEvent, QDropEvent, QMouseEvent
from PyQt5.QtWidgets import QDialog, QMenu, QMessageBox, QTreeWidget, QTreeWidgetItem from PyQt5.QtWidgets import QMenu, QMessageBox, QTreeWidget, QTreeWidgetItem
from novelwriter import CONFIG, SHARED from novelwriter import CONFIG, SHARED
from novelwriter.core.item import NWItem from novelwriter.core.item import NWItem
@@ -37,7 +37,7 @@ from novelwriter.dialogs.editlabel import GuiEditLabel
from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType, nwWidget from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType, nwWidget
from novelwriter.gui.projtree import GuiProjectTree, GuiProjectView, _TreeContextMenu from novelwriter.gui.projtree import GuiProjectTree, GuiProjectView, _TreeContextMenu
from novelwriter.guimain import GuiMain from novelwriter.guimain import GuiMain
from novelwriter.types import QtModeNone, QtMouseLeft, QtMouseMiddle from novelwriter.types import QtAccepted, QtModeNone, QtMouseLeft, QtMouseMiddle, QtRejected
from tests.mocked import causeOSError from tests.mocked import causeOSError
from tests.tools import C, buildTestProject from tests.tools import C, buildTestProject
@@ -529,8 +529,9 @@ def testGuiProjTree_MergeDocuments(qtbot, monkeypatch, nwGUI, projPath, mockRnd,
monkeypatch.setattr(GuiDocMerge, "__init__", lambda *a: None) monkeypatch.setattr(GuiDocMerge, "__init__", lambda *a: None)
monkeypatch.setattr(GuiDocMerge, "exec", lambda *a: None) monkeypatch.setattr(GuiDocMerge, "exec", lambda *a: None)
monkeypatch.setattr(GuiDocMerge, "result", lambda *a: QDialog.DialogCode.Accepted) monkeypatch.setattr(GuiDocMerge, "softDelete", lambda *a: None)
monkeypatch.setattr(GuiDocMerge, "getData", lambda *a: mergeData) monkeypatch.setattr(GuiDocMerge, "result", lambda *a: QtAccepted)
monkeypatch.setattr(GuiDocMerge, "data", lambda *a: mergeData)
buildTestProject(nwGUI, projPath) buildTestProject(nwGUI, projPath)
@@ -585,7 +586,7 @@ def testGuiProjTree_MergeDocuments(qtbot, monkeypatch, nwGUI, projPath, mockRnd,
# User cancels merge # User cancels merge
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr(GuiDocMerge, "result", lambda *a: QDialog.DialogCode.Rejected) mp.setattr(GuiDocMerge, "result", lambda *a: QtRejected)
assert projTree._mergeDocuments(hChapter1, True) is False assert projTree._mergeDocuments(hChapter1, True) is False
# The merge goes through # The merge goes through
@@ -628,8 +629,9 @@ def testGuiProjTree_SplitDocument(qtbot, monkeypatch, nwGUI, projPath, mockRnd,
monkeypatch.setattr(GuiDocSplit, "__init__", lambda *a: None) monkeypatch.setattr(GuiDocSplit, "__init__", lambda *a: None)
monkeypatch.setattr(GuiDocSplit, "exec", lambda *a: None) monkeypatch.setattr(GuiDocSplit, "exec", lambda *a: None)
monkeypatch.setattr(GuiDocSplit, "result", lambda *a: QDialog.DialogCode.Accepted) monkeypatch.setattr(GuiDocSplit, "softDelete", lambda *a: None)
monkeypatch.setattr(GuiDocSplit, "getData", lambda *a: (splitData, splitText)) monkeypatch.setattr(GuiDocSplit, "result", lambda *a: QtAccepted)
monkeypatch.setattr(GuiDocSplit, "data", lambda *a: (splitData, splitText))
# Create a project # Create a project
buildTestProject(nwGUI, projPath) buildTestProject(nwGUI, projPath)
@@ -722,7 +724,7 @@ def testGuiProjTree_SplitDocument(qtbot, monkeypatch, nwGUI, projPath, mockRnd,
# Cancelled by user # Cancelled by user
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr(GuiDocSplit, "result", lambda *a: QDialog.DialogCode.Rejected) mp.setattr(GuiDocSplit, "result", lambda *a: QtRejected)
assert projTree._splitDocument(hSplitDoc) is False assert projTree._splitDocument(hSplitDoc) is False
# qtbot.stop() # qtbot.stop()
-1
View File
@@ -70,7 +70,6 @@ def testToolWelcome_Main(qtbot: QtBot, monkeypatch, nwGUI, fncPath):
def testToolWelcome_Open(qtbot: QtBot, monkeypatch, nwGUI, fncPath): def testToolWelcome_Open(qtbot: QtBot, monkeypatch, nwGUI, fncPath):
"""Test the open tab in the Welcome window.""" """Test the open tab in the Welcome window."""
monkeypatch.setattr(QMenu, "exec", lambda *a: None) monkeypatch.setattr(QMenu, "exec", lambda *a: None)
monkeypatch.setattr(QMenu, "deleteLater", lambda *a: None)
CONFIG.recentProjects.update("/stuff/project_one", "Project One", 12345, 1690000000) CONFIG.recentProjects.update("/stuff/project_one", "Project One", 12345, 1690000000)
CONFIG.recentProjects.update("/stuff/project_two", "Project Two", 54321, 1700000000) CONFIG.recentProjects.update("/stuff/project_two", "Project Two", 54321, 1700000000)