Add deleteLater to temporary widgets (#1629)
This commit is contained in:
@@ -76,6 +76,7 @@ def main(sysArgs: list | None = None):
|
||||
"config=",
|
||||
"data=",
|
||||
"testmode",
|
||||
"meminfo"
|
||||
]
|
||||
|
||||
helpMsg = (
|
||||
@@ -92,6 +93,7 @@ def main(sysArgs: list | None = None):
|
||||
" -v, --version Print program version and exit.\n"
|
||||
" --info Print additional runtime information.\n"
|
||||
" --debug Print debug output. Includes --info.\n"
|
||||
" --meminfo Show memory usage information in the status bar.\n"
|
||||
" --style= Sets Qt5 style flag. Defaults to 'Fusion'.\n"
|
||||
" --config= Alternative config file.\n"
|
||||
" --data= Alternative user data path.\n"
|
||||
@@ -127,6 +129,7 @@ def main(sysArgs: list | None = None):
|
||||
elif inOpt == "--info":
|
||||
logLevel = logging.INFO
|
||||
elif inOpt == "--debug":
|
||||
CONFIG.isDebug = True
|
||||
logLevel = logging.DEBUG
|
||||
logFormat = "[{asctime:}] {filename:>17}:{lineno:<4d} {levelname:8} {message:}"
|
||||
elif inOpt == "--style":
|
||||
@@ -137,6 +140,8 @@ def main(sysArgs: list | None = None):
|
||||
dataPath = inArg
|
||||
elif inOpt == "--testmode":
|
||||
testMode = True
|
||||
elif inOpt == "--meminfo":
|
||||
CONFIG.memInfo = True
|
||||
|
||||
# Setup Logging
|
||||
pkgLogger = logging.getLogger(__package__)
|
||||
|
||||
@@ -225,7 +225,8 @@ class Config:
|
||||
# Other System Info
|
||||
self.hostName = QSysInfo.machineHostName()
|
||||
self.kernelVer = QSysInfo.kernelVersion()
|
||||
self.isDebug = False
|
||||
self.isDebug = False # True if running in debug mode
|
||||
self.memInfo = False # True if displaying mem info in status bar
|
||||
|
||||
# Packages
|
||||
self.hasEnchant = False # The pyenchant package
|
||||
@@ -485,7 +486,6 @@ class Config:
|
||||
|
||||
self._recentObj.loadCache()
|
||||
self._checkOptionalPackages()
|
||||
self.isDebug = logger.getEffectiveLevel() == logging.DEBUG
|
||||
|
||||
logger.debug("Config instance initialised")
|
||||
|
||||
|
||||
@@ -551,7 +551,7 @@ class NWIndex:
|
||||
return 0
|
||||
|
||||
def getTableOfContents(
|
||||
self, rHandle: str, maxDepth: int, skipExcl: bool = True
|
||||
self, rHandle: str | None, maxDepth: int, skipExcl: bool = True
|
||||
) -> list[tuple[str, int, str, int]]:
|
||||
"""Generate a table of contents up to a maximum depth."""
|
||||
tOrder = []
|
||||
|
||||
@@ -28,7 +28,7 @@ import novelwriter
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from PyQt5.QtGui import QCursor
|
||||
from PyQt5.QtGui import QCloseEvent, QCursor
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtWidgets import (
|
||||
qApp, QDialog, QDialogButtonBox, QHBoxLayout, QLabel, QTabWidget,
|
||||
@@ -44,7 +44,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class GuiAbout(QDialog):
|
||||
|
||||
def __init__(self, parent: QWidget):
|
||||
def __init__(self, parent: QWidget) -> None:
|
||||
super().__init__(parent=parent)
|
||||
|
||||
logger.debug("Create: GuiAbout")
|
||||
@@ -101,7 +101,7 @@ class GuiAbout(QDialog):
|
||||
|
||||
# OK Button
|
||||
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok)
|
||||
self.buttonBox.accepted.connect(self._doClose)
|
||||
self.buttonBox.accepted.connect(self.close)
|
||||
|
||||
self.outerBox.addLayout(self.innerBox)
|
||||
self.outerBox.addWidget(self.buttonBox)
|
||||
@@ -111,13 +111,12 @@ class GuiAbout(QDialog):
|
||||
|
||||
return
|
||||
|
||||
def __del__(self): # pragma: no cover
|
||||
def __del__(self) -> None: # pragma: no cover
|
||||
logger.debug("Delete: GuiAbout")
|
||||
return
|
||||
|
||||
def populateGUI(self):
|
||||
"""Populate tabs with text.
|
||||
"""
|
||||
def populateGUI(self) -> None:
|
||||
"""Populate tabs with text."""
|
||||
qApp.setOverrideCursor(QCursor(Qt.WaitCursor))
|
||||
self._setStyleSheet()
|
||||
self._fillAboutPage()
|
||||
@@ -127,19 +126,27 @@ class GuiAbout(QDialog):
|
||||
qApp.restoreOverrideCursor()
|
||||
return
|
||||
|
||||
def showReleaseNotes(self):
|
||||
"""Show the release notes.
|
||||
"""
|
||||
def showReleaseNotes(self) -> None:
|
||||
"""Show the release notes."""
|
||||
self.tabBox.setCurrentWidget(self.pageNotes)
|
||||
return
|
||||
|
||||
##
|
||||
# Events
|
||||
##
|
||||
|
||||
def closeEvent(self, event: QCloseEvent) -> None:
|
||||
"""Capture the close event and perform cleanup."""
|
||||
event.accept()
|
||||
self.deleteLater()
|
||||
return
|
||||
|
||||
##
|
||||
# Internal Functions
|
||||
##
|
||||
|
||||
def _fillAboutPage(self):
|
||||
"""Generate the content for the About page.
|
||||
"""
|
||||
def _fillAboutPage(self) -> None:
|
||||
"""Generate the content for the About page."""
|
||||
aboutMsg = (
|
||||
"<h2>{title1}</h2>"
|
||||
"<p>{copy}</p>"
|
||||
@@ -181,9 +188,8 @@ class GuiAbout(QDialog):
|
||||
|
||||
return
|
||||
|
||||
def _fillNotesPage(self):
|
||||
"""Load the content for the Release Notes page.
|
||||
"""
|
||||
def _fillNotesPage(self) -> None:
|
||||
"""Load the content for the Release Notes page."""
|
||||
docPath = CONFIG.assetPath("text") / "release_notes.htm"
|
||||
docText = readTextFile(docPath)
|
||||
if docText:
|
||||
@@ -192,9 +198,8 @@ class GuiAbout(QDialog):
|
||||
self.pageNotes.setHtml("Error loading release notes text ...")
|
||||
return
|
||||
|
||||
def _fillCreditsPage(self):
|
||||
"""Load the content for the Credits page.
|
||||
"""
|
||||
def _fillCreditsPage(self) -> None:
|
||||
"""Load the content for the Credits page."""
|
||||
docPath = CONFIG.assetPath("text") / "credits_en.htm"
|
||||
docText = readTextFile(docPath)
|
||||
if docText:
|
||||
@@ -203,9 +208,8 @@ class GuiAbout(QDialog):
|
||||
self.pageCredits.setHtml("Error loading credits text ...")
|
||||
return
|
||||
|
||||
def _fillLicensePage(self):
|
||||
"""Load the content for the Licence page.
|
||||
"""
|
||||
def _fillLicensePage(self) -> None:
|
||||
"""Load the content for the Licence page."""
|
||||
docPath = CONFIG.assetPath("text") / "gplv3_en.htm"
|
||||
docText = readTextFile(docPath)
|
||||
if docText:
|
||||
@@ -214,9 +218,8 @@ class GuiAbout(QDialog):
|
||||
self.pageLicense.setHtml("Error loading licence text ...")
|
||||
return
|
||||
|
||||
def _setStyleSheet(self):
|
||||
"""Set stylesheet for all browser tabs
|
||||
"""
|
||||
def _setStyleSheet(self) -> None:
|
||||
"""Set stylesheet for all browser tabs."""
|
||||
styleSheet = (
|
||||
"h1, h2, h3, h4 {{"
|
||||
" color: rgb({hColR},{hColG},{hColB});"
|
||||
@@ -242,8 +245,4 @@ class GuiAbout(QDialog):
|
||||
|
||||
return
|
||||
|
||||
def _doClose(self):
|
||||
self.close()
|
||||
return
|
||||
|
||||
# END Class GuiAbout
|
||||
|
||||
@@ -26,7 +26,8 @@ from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from PyQt5.QtCore import Qt, QSize
|
||||
from PyQt5.QtGui import QCloseEvent
|
||||
from PyQt5.QtCore import Qt, QSize, pyqtSlot
|
||||
from PyQt5.QtWidgets import (
|
||||
QAbstractItemView, QDialog, QDialogButtonBox, QGridLayout, QLabel,
|
||||
QListWidget, QListWidgetItem, QVBoxLayout, QWidget
|
||||
@@ -108,13 +109,12 @@ class GuiDocMerge(QDialog):
|
||||
|
||||
return
|
||||
|
||||
def __del__(self): # pragma: no cover
|
||||
def __del__(self) -> None: # pragma: no cover
|
||||
logger.debug("Delete: GuiDocMerge")
|
||||
return
|
||||
|
||||
def getData(self):
|
||||
"""Return the user's choices.
|
||||
"""
|
||||
def getData(self) -> dict:
|
||||
"""Return the user's choices."""
|
||||
finalItems = []
|
||||
for i in range(self.listBox.count()):
|
||||
item = self.listBox.item(i)
|
||||
@@ -127,12 +127,22 @@ class GuiDocMerge(QDialog):
|
||||
return self._data
|
||||
|
||||
##
|
||||
# Slots
|
||||
# Events
|
||||
##
|
||||
|
||||
def _resetList(self):
|
||||
"""Reset the content of the list box to its original state.
|
||||
"""
|
||||
def closeEvent(self, event: QCloseEvent) -> None:
|
||||
"""Capture the close event and perform cleanup."""
|
||||
event.accept()
|
||||
self.deleteLater()
|
||||
return
|
||||
|
||||
##
|
||||
# Private Slots
|
||||
##
|
||||
|
||||
@pyqtSlot()
|
||||
def _resetList(self) -> None:
|
||||
"""Reset the content of the list box to its original state."""
|
||||
logger.debug("Resetting list box content")
|
||||
sHandle = self._data.get("sHandle", None)
|
||||
itemList = self._data.get("origItems", [])
|
||||
@@ -143,9 +153,8 @@ class GuiDocMerge(QDialog):
|
||||
# Internal Functions
|
||||
##
|
||||
|
||||
def _loadContent(self, sHandle, itemList):
|
||||
"""Load content from a given list of items.
|
||||
"""
|
||||
def _loadContent(self, sHandle: str, itemList: list[str]) -> None:
|
||||
"""Load content from a given list of items."""
|
||||
self._data = {}
|
||||
self._data["sHandle"] = sHandle
|
||||
self._data["origItems"] = itemList
|
||||
|
||||
@@ -26,10 +26,11 @@ from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtGui import QCloseEvent
|
||||
from PyQt5.QtCore import Qt, pyqtSlot
|
||||
from PyQt5.QtWidgets import (
|
||||
QDialog, QVBoxLayout, QComboBox, QListWidget, QAbstractItemView,
|
||||
QListWidgetItem, QDialogButtonBox, QLabel, QGridLayout
|
||||
QAbstractItemView, QComboBox, QDialog, QDialogButtonBox, QGridLayout,
|
||||
QLabel, QListWidget, QListWidgetItem, QVBoxLayout, QWidget
|
||||
)
|
||||
|
||||
from novelwriter import CONFIG, SHARED
|
||||
@@ -45,7 +46,7 @@ class GuiDocSplit(QDialog):
|
||||
LEVEL_ROLE = Qt.ItemDataRole.UserRole + 1
|
||||
LABEL_ROLE = Qt.ItemDataRole.UserRole + 2
|
||||
|
||||
def __init__(self, parent, sHandle):
|
||||
def __init__(self, parent: QWidget, sHandle: str) -> None:
|
||||
super().__init__(parent=parent)
|
||||
|
||||
logger.debug("Create: GuiDocSplit")
|
||||
@@ -138,11 +139,11 @@ class GuiDocSplit(QDialog):
|
||||
|
||||
return
|
||||
|
||||
def __del__(self): # pragma: no cover
|
||||
def __del__(self) -> None: # pragma: no cover
|
||||
logger.debug("Delete: GuiDocSplit")
|
||||
return
|
||||
|
||||
def getData(self):
|
||||
def getData(self) -> tuple[dict, list]:
|
||||
"""Return the user's choices. Also save the users options for
|
||||
the next time the dialog is used.
|
||||
"""
|
||||
@@ -167,6 +168,7 @@ class GuiDocSplit(QDialog):
|
||||
self._data["docHierarchy"] = docHierarchy
|
||||
self._data["moveToTrash"] = moveToTrash
|
||||
|
||||
logger.debug("Saving State: GuiDocSplit")
|
||||
pOptions = SHARED.project.options
|
||||
pOptions.setValue("GuiDocSplit", "spLevel", spLevel)
|
||||
pOptions.setValue("GuiDocSplit", "intoFolder", intoFolder)
|
||||
@@ -175,12 +177,22 @@ class GuiDocSplit(QDialog):
|
||||
return self._data, self._text
|
||||
|
||||
##
|
||||
# Slots
|
||||
# Events
|
||||
##
|
||||
|
||||
def _reloadList(self):
|
||||
"""Reload the content of the list box.
|
||||
"""
|
||||
def closeEvent(self, event: QCloseEvent) -> None:
|
||||
"""Capture the close event and perform cleanup."""
|
||||
event.accept()
|
||||
self.deleteLater()
|
||||
return
|
||||
|
||||
##
|
||||
# Private Slots
|
||||
##
|
||||
|
||||
@pyqtSlot()
|
||||
def _reloadList(self) -> None:
|
||||
"""Reload the content of the list box."""
|
||||
sHandle = self._data.get("sHandle", None)
|
||||
self._loadContent(sHandle)
|
||||
return
|
||||
@@ -189,9 +201,8 @@ class GuiDocSplit(QDialog):
|
||||
# Internal Functions
|
||||
##
|
||||
|
||||
def _loadContent(self, sHandle):
|
||||
"""Load content from a given source item.
|
||||
"""
|
||||
def _loadContent(self, sHandle: str) -> None:
|
||||
"""Load content from a given source item."""
|
||||
self._data = {}
|
||||
self._data["sHandle"] = sHandle
|
||||
|
||||
|
||||
@@ -26,7 +26,8 @@ from __future__ import annotations
|
||||
import logging
|
||||
|
||||
from PyQt5.QtWidgets import (
|
||||
QDialog, QVBoxLayout, QLineEdit, QLabel, QDialogButtonBox, QHBoxLayout
|
||||
QDialog, QDialogButtonBox, QHBoxLayout, QLabel, QLineEdit, QVBoxLayout,
|
||||
QWidget
|
||||
)
|
||||
|
||||
from novelwriter import CONFIG
|
||||
@@ -36,9 +37,10 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class GuiEditLabel(QDialog):
|
||||
|
||||
def __init__(self, parent, text=""):
|
||||
def __init__(self, parent: QWidget, text: str = "") -> None:
|
||||
super().__init__(parent=parent)
|
||||
|
||||
logger.debug("Create: GuiEditLabel")
|
||||
self.setObjectName("GuiEditLabel")
|
||||
self.setWindowTitle(self.tr("Item Label"))
|
||||
|
||||
@@ -70,16 +72,26 @@ class GuiEditLabel(QDialog):
|
||||
|
||||
self.setLayout(self.outerBox)
|
||||
|
||||
logger.debug("Ready: GuiEditLabel")
|
||||
|
||||
return
|
||||
|
||||
def __del__(self) -> None: # pragma: no cover
|
||||
logger.debug("Delete: GuiEditLabel")
|
||||
return
|
||||
|
||||
@property
|
||||
def itemLabel(self):
|
||||
def itemLabel(self) -> str:
|
||||
return self.labelValue.text()
|
||||
|
||||
@classmethod
|
||||
def getLabel(cls, parent, text):
|
||||
def getLabel(cls, parent: QWidget, text: str) -> tuple[str, bool]:
|
||||
"""Pop the dialog and return the result."""
|
||||
cls = GuiEditLabel(parent, text=text)
|
||||
cls.exec_()
|
||||
return cls.itemLabel, cls.result() == QDialog.Accepted
|
||||
label = cls.itemLabel
|
||||
accepted = cls.result() == QDialog.Accepted
|
||||
cls.deleteLater()
|
||||
return label, accepted
|
||||
|
||||
# END Class GuiEditLabel
|
||||
|
||||
@@ -25,11 +25,11 @@ from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from PyQt5.QtGui import QFont
|
||||
from PyQt5.QtCore import Qt, pyqtSlot
|
||||
from PyQt5.QtGui import QCloseEvent, QFont
|
||||
from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot
|
||||
from PyQt5.QtWidgets import (
|
||||
QDialog, QWidget, QComboBox, QSpinBox, QPushButton, QDialogButtonBox,
|
||||
QLineEdit, QFileDialog, QFontDialog, QDoubleSpinBox
|
||||
QLineEdit, QFileDialog, QFontDialog, QDoubleSpinBox, qApp
|
||||
)
|
||||
|
||||
from novelwriter import CONFIG, SHARED
|
||||
@@ -43,6 +43,8 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class GuiPreferences(NPagedDialog):
|
||||
|
||||
newPreferencesReady = pyqtSignal(bool, bool, bool, bool)
|
||||
|
||||
def __init__(self, parent: QWidget) -> None:
|
||||
super().__init__(parent=parent)
|
||||
|
||||
@@ -66,9 +68,10 @@ class GuiPreferences(NPagedDialog):
|
||||
self.addTab(self.tabAuto, self.tr("Automation"))
|
||||
self.addTab(self.tabQuote, self.tr("Quotes"))
|
||||
|
||||
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
|
||||
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel, self)
|
||||
self.buttonBox.accepted.connect(self._doSave)
|
||||
self.buttonBox.rejected.connect(self._doClose)
|
||||
self.buttonBox.rejected.connect(self.close)
|
||||
self.rejected.connect(self.close)
|
||||
self.addControls(self.buttonBox)
|
||||
|
||||
self.resize(*CONFIG.preferencesWinSize)
|
||||
@@ -83,29 +86,21 @@ class GuiPreferences(NPagedDialog):
|
||||
|
||||
return
|
||||
|
||||
def __del__(self): # pragma: no cover
|
||||
def __del__(self) -> None: # pragma: no cover
|
||||
logger.debug("Delete: GuiPreferences")
|
||||
return
|
||||
|
||||
##
|
||||
# Properties
|
||||
# Events
|
||||
##
|
||||
|
||||
@property
|
||||
def updateTheme(self) -> bool:
|
||||
return self._updateTheme
|
||||
|
||||
@property
|
||||
def updateSyntax(self) -> bool:
|
||||
return self._updateSyntax
|
||||
|
||||
@property
|
||||
def needsRestart(self) -> bool:
|
||||
return self._needsRestart
|
||||
|
||||
@property
|
||||
def refreshTree(self) -> bool:
|
||||
return self._refreshTree
|
||||
def closeEvent(self, event: QCloseEvent) -> None:
|
||||
"""Capture the close event and perform cleanup."""
|
||||
logger.debug("Close: GuiPreferences")
|
||||
self._saveWindowSize()
|
||||
event.accept()
|
||||
self.deleteLater()
|
||||
return
|
||||
|
||||
##
|
||||
# Private Slots
|
||||
@@ -113,11 +108,7 @@ class GuiPreferences(NPagedDialog):
|
||||
|
||||
@pyqtSlot()
|
||||
def _doSave(self) -> None:
|
||||
"""Trigger all the save functions in the tabs, and collect the
|
||||
status of the saves.
|
||||
"""
|
||||
logger.debug("Saving new preferences")
|
||||
|
||||
"""Trigger save functions in the tabs and emit ready signal."""
|
||||
self.tabGeneral.saveValues()
|
||||
self.tabProjects.saveValues()
|
||||
self.tabDocs.saveValues()
|
||||
@@ -126,19 +117,15 @@ class GuiPreferences(NPagedDialog):
|
||||
self.tabAuto.saveValues()
|
||||
self.tabQuote.saveValues()
|
||||
|
||||
self._saveWindowSize()
|
||||
CONFIG.saveConfig()
|
||||
self.accept()
|
||||
self.newPreferencesReady.emit(
|
||||
self._needsRestart, self._refreshTree, self._updateTheme, self._updateSyntax
|
||||
)
|
||||
qApp.processEvents()
|
||||
self.close()
|
||||
|
||||
return
|
||||
|
||||
@pyqtSlot()
|
||||
def _doClose(self) -> None:
|
||||
"""Close the preferences without saving the changes."""
|
||||
self._saveWindowSize()
|
||||
self.reject()
|
||||
return
|
||||
|
||||
##
|
||||
# Internal Functions
|
||||
##
|
||||
@@ -169,7 +156,7 @@ class GuiPreferencesGeneral(QWidget):
|
||||
minWidth = CONFIG.pxInt(200)
|
||||
|
||||
# Select Locale
|
||||
self.guiLocale = QComboBox()
|
||||
self.guiLocale = QComboBox(self)
|
||||
self.guiLocale.setMinimumWidth(minWidth)
|
||||
theLangs = CONFIG.listLanguages(CONFIG.LANG_NW)
|
||||
for lang, langName in theLangs:
|
||||
@@ -187,7 +174,7 @@ class GuiPreferencesGeneral(QWidget):
|
||||
)
|
||||
|
||||
# Select Theme
|
||||
self.guiTheme = QComboBox()
|
||||
self.guiTheme = QComboBox(self)
|
||||
self.guiTheme.setMinimumWidth(minWidth)
|
||||
self.theThemes = SHARED.theme.listThemes()
|
||||
for themeDir, themeName in self.theThemes:
|
||||
@@ -203,7 +190,7 @@ class GuiPreferencesGeneral(QWidget):
|
||||
)
|
||||
|
||||
# Editor Theme
|
||||
self.guiSyntax = QComboBox()
|
||||
self.guiSyntax = QComboBox(self)
|
||||
self.guiSyntax.setMinimumWidth(CONFIG.pxInt(200))
|
||||
self.theSyntaxes = SHARED.theme.listSyntax()
|
||||
for syntaxFile, syntaxName in self.theSyntaxes:
|
||||
@@ -219,11 +206,11 @@ class GuiPreferencesGeneral(QWidget):
|
||||
)
|
||||
|
||||
# Font Family
|
||||
self.guiFont = QLineEdit()
|
||||
self.guiFont = QLineEdit(self)
|
||||
self.guiFont.setReadOnly(True)
|
||||
self.guiFont.setFixedWidth(CONFIG.pxInt(162))
|
||||
self.guiFont.setText(CONFIG.guiFont)
|
||||
self.fontButton = QPushButton("...")
|
||||
self.fontButton = QPushButton("...", self)
|
||||
self.fontButton.setMaximumWidth(int(2.5*SHARED.theme.getTextWidth("...")))
|
||||
self.fontButton.clicked.connect(self._selectFont)
|
||||
self.mainForm.addRow(
|
||||
@@ -378,7 +365,7 @@ class GuiPreferencesProjects(QWidget):
|
||||
|
||||
# Backup Path
|
||||
self.backupPath = CONFIG.backupPath()
|
||||
self.backupGetPath = QPushButton(self.tr("Browse"))
|
||||
self.backupGetPath = QPushButton(self.tr("Browse"), self)
|
||||
self.backupGetPath.clicked.connect(self._backupFolder)
|
||||
self.backupPathRow = self.mainForm.addRow(
|
||||
self.tr("Backup storage location"),
|
||||
@@ -421,7 +408,7 @@ class GuiPreferencesProjects(QWidget):
|
||||
)
|
||||
|
||||
# Inactive time for idle
|
||||
self.userIdleTime = QDoubleSpinBox()
|
||||
self.userIdleTime = QDoubleSpinBox(self)
|
||||
self.userIdleTime.setMinimum(0.5)
|
||||
self.userIdleTime.setMaximum(600.0)
|
||||
self.userIdleTime.setSingleStep(0.5)
|
||||
@@ -496,11 +483,11 @@ class GuiPreferencesDocuments(QWidget):
|
||||
self.mainForm.addGroupLabel(self.tr("Text Style"))
|
||||
|
||||
# Font Family
|
||||
self.textFont = QLineEdit()
|
||||
self.textFont = QLineEdit(self)
|
||||
self.textFont.setReadOnly(True)
|
||||
self.textFont.setFixedWidth(CONFIG.pxInt(162))
|
||||
self.textFont.setText(CONFIG.textFont)
|
||||
self.fontButton = QPushButton("...")
|
||||
self.fontButton = QPushButton("...", self)
|
||||
self.fontButton.setMaximumWidth(int(2.5*SHARED.theme.getTextWidth("...")))
|
||||
self.fontButton.clicked.connect(self._selectFont)
|
||||
self.mainForm.addRow(
|
||||
@@ -960,7 +947,7 @@ class GuiPreferencesAutomation(QWidget):
|
||||
self.mainForm.addGroupLabel(self.tr("Automatic Padding"))
|
||||
|
||||
# Pad Before
|
||||
self.fmtPadBefore = QLineEdit()
|
||||
self.fmtPadBefore = QLineEdit(self)
|
||||
self.fmtPadBefore.setMaxLength(32)
|
||||
self.fmtPadBefore.setText(CONFIG.fmtPadBefore)
|
||||
self.mainForm.addRow(
|
||||
@@ -970,7 +957,7 @@ class GuiPreferencesAutomation(QWidget):
|
||||
)
|
||||
|
||||
# Pad After
|
||||
self.fmtPadAfter = QLineEdit()
|
||||
self.fmtPadAfter = QLineEdit(self)
|
||||
self.fmtPadAfter.setMaxLength(32)
|
||||
self.fmtPadAfter.setText(CONFIG.fmtPadAfter)
|
||||
self.mainForm.addRow(
|
||||
@@ -1046,13 +1033,13 @@ class GuiPreferencesQuotes(QWidget):
|
||||
self.quoteSym = {}
|
||||
|
||||
# Single Quote Style
|
||||
self.quoteSym["SO"] = QLineEdit()
|
||||
self.quoteSym["SO"] = QLineEdit(self)
|
||||
self.quoteSym["SO"].setMaxLength(1)
|
||||
self.quoteSym["SO"].setReadOnly(True)
|
||||
self.quoteSym["SO"].setFixedWidth(qWidth)
|
||||
self.quoteSym["SO"].setAlignment(Qt.AlignCenter)
|
||||
self.quoteSym["SO"].setText(CONFIG.fmtSQuoteOpen)
|
||||
self.btnSingleStyleO = QPushButton("...")
|
||||
self.btnSingleStyleO = QPushButton("...", self)
|
||||
self.btnSingleStyleO.setMaximumWidth(bWidth)
|
||||
self.btnSingleStyleO.clicked.connect(lambda: self._getQuote("SO"))
|
||||
self.mainForm.addRow(
|
||||
@@ -1062,13 +1049,13 @@ class GuiPreferencesQuotes(QWidget):
|
||||
button=self.btnSingleStyleO
|
||||
)
|
||||
|
||||
self.quoteSym["SC"] = QLineEdit()
|
||||
self.quoteSym["SC"] = QLineEdit(self)
|
||||
self.quoteSym["SC"].setMaxLength(1)
|
||||
self.quoteSym["SC"].setReadOnly(True)
|
||||
self.quoteSym["SC"].setFixedWidth(qWidth)
|
||||
self.quoteSym["SC"].setAlignment(Qt.AlignCenter)
|
||||
self.quoteSym["SC"].setText(CONFIG.fmtSQuoteClose)
|
||||
self.btnSingleStyleC = QPushButton("...")
|
||||
self.btnSingleStyleC = QPushButton("...", self)
|
||||
self.btnSingleStyleC.setMaximumWidth(bWidth)
|
||||
self.btnSingleStyleC.clicked.connect(lambda: self._getQuote("SC"))
|
||||
self.mainForm.addRow(
|
||||
@@ -1079,13 +1066,13 @@ class GuiPreferencesQuotes(QWidget):
|
||||
)
|
||||
|
||||
# Double Quote Style
|
||||
self.quoteSym["DO"] = QLineEdit()
|
||||
self.quoteSym["DO"] = QLineEdit(self)
|
||||
self.quoteSym["DO"].setMaxLength(1)
|
||||
self.quoteSym["DO"].setReadOnly(True)
|
||||
self.quoteSym["DO"].setFixedWidth(qWidth)
|
||||
self.quoteSym["DO"].setAlignment(Qt.AlignCenter)
|
||||
self.quoteSym["DO"].setText(CONFIG.fmtDQuoteOpen)
|
||||
self.btnDoubleStyleO = QPushButton("...")
|
||||
self.btnDoubleStyleO = QPushButton("...", self)
|
||||
self.btnDoubleStyleO.setMaximumWidth(bWidth)
|
||||
self.btnDoubleStyleO.clicked.connect(lambda: self._getQuote("DO"))
|
||||
self.mainForm.addRow(
|
||||
@@ -1095,13 +1082,13 @@ class GuiPreferencesQuotes(QWidget):
|
||||
button=self.btnDoubleStyleO
|
||||
)
|
||||
|
||||
self.quoteSym["DC"] = QLineEdit()
|
||||
self.quoteSym["DC"] = QLineEdit(self)
|
||||
self.quoteSym["DC"].setMaxLength(1)
|
||||
self.quoteSym["DC"].setReadOnly(True)
|
||||
self.quoteSym["DC"].setFixedWidth(qWidth)
|
||||
self.quoteSym["DC"].setAlignment(Qt.AlignCenter)
|
||||
self.quoteSym["DC"].setText(CONFIG.fmtDQuoteClose)
|
||||
self.btnDoubleStyleC = QPushButton("...")
|
||||
self.btnDoubleStyleC = QPushButton("...", self)
|
||||
self.btnDoubleStyleC.setMaximumWidth(bWidth)
|
||||
self.btnDoubleStyleC.clicked.connect(lambda: self._getQuote("DC"))
|
||||
self.mainForm.addRow(
|
||||
|
||||
@@ -26,8 +26,8 @@ from __future__ import annotations
|
||||
import math
|
||||
import logging
|
||||
|
||||
from PyQt5.QtGui import QCloseEvent, QFont
|
||||
from PyQt5.QtCore import Qt, QSize, pyqtSlot
|
||||
from PyQt5.QtGui import QFont
|
||||
from PyQt5.QtWidgets import (
|
||||
QAbstractItemView, QDialogButtonBox, QGridLayout, QHBoxLayout, QLabel,
|
||||
QLineEdit, QSpinBox, QTreeWidget, QTreeWidgetItem, QVBoxLayout, QWidget
|
||||
@@ -45,7 +45,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class GuiProjectDetails(NPagedDialog):
|
||||
|
||||
def __init__(self, parent):
|
||||
def __init__(self, parent: QWidget) -> None:
|
||||
super().__init__(parent=parent)
|
||||
|
||||
logger.debug("Create: GuiProjectDetails")
|
||||
@@ -71,43 +71,41 @@ class GuiProjectDetails(NPagedDialog):
|
||||
self.addTab(self.tabContents, self.tr("Contents"))
|
||||
|
||||
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Close)
|
||||
self.buttonBox.button(QDialogButtonBox.Close)
|
||||
self.buttonBox.rejected.connect(self._doClose)
|
||||
self.buttonBox.rejected.connect(self.close)
|
||||
self.rejected.connect(self.close)
|
||||
self.addControls(self.buttonBox)
|
||||
|
||||
logger.debug("Ready: GuiProjectDetails")
|
||||
|
||||
return
|
||||
|
||||
def __del__(self): # pragma: no cover
|
||||
def __del__(self) -> None: # pragma: no cover
|
||||
logger.debug("Delete: GuiProjectDetails")
|
||||
return
|
||||
|
||||
def updateValues(self):
|
||||
"""Set all the values of the pages.
|
||||
"""
|
||||
def updateValues(self) -> None:
|
||||
"""Set all the values of the pages."""
|
||||
self.tabMain.updateValues()
|
||||
self.tabContents.updateValues()
|
||||
return
|
||||
|
||||
##
|
||||
# Slots
|
||||
# Events
|
||||
##
|
||||
|
||||
def _doClose(self):
|
||||
"""Save settings and close the dialog.
|
||||
"""
|
||||
def closeEvent(self, event: QCloseEvent) -> None:
|
||||
"""Capture the close event and perform cleanup."""
|
||||
self._saveGuiSettings()
|
||||
self.close()
|
||||
event.accept()
|
||||
self.deleteLater()
|
||||
return
|
||||
|
||||
##
|
||||
# Internal Functions
|
||||
##
|
||||
|
||||
def _saveGuiSettings(self):
|
||||
"""Save GUI settings.
|
||||
"""
|
||||
def _saveGuiSettings(self) -> None:
|
||||
"""Save GUI settings."""
|
||||
winWidth = CONFIG.rpxInt(self.width())
|
||||
winHeight = CONFIG.rpxInt(self.height())
|
||||
|
||||
@@ -122,6 +120,7 @@ class GuiProjectDetails(NPagedDialog):
|
||||
countFrom = self.tabContents.poValue.value()
|
||||
clearDouble = self.tabContents.dblValue.isChecked()
|
||||
|
||||
logger.debug("Saving State: GuiProjectDetails")
|
||||
pOptions = SHARED.project.options
|
||||
pOptions.setValue("GuiProjectDetails", "winWidth", winWidth)
|
||||
pOptions.setValue("GuiProjectDetails", "winHeight", winHeight)
|
||||
@@ -141,7 +140,7 @@ class GuiProjectDetails(NPagedDialog):
|
||||
|
||||
class GuiProjectDetailsMain(QWidget):
|
||||
|
||||
def __init__(self, parent):
|
||||
def __init__(self, parent: QWidget) -> None:
|
||||
super().__init__(parent=parent)
|
||||
|
||||
fPx = SHARED.theme.fontPixelSize
|
||||
@@ -270,7 +269,7 @@ class GuiProjectDetailsContents(QWidget):
|
||||
C_PAGE = 3
|
||||
C_PROG = 4
|
||||
|
||||
def __init__(self, parent):
|
||||
def __init__(self, parent: QWidget) -> None:
|
||||
super().__init__(parent=parent)
|
||||
|
||||
# Internal
|
||||
@@ -406,9 +405,8 @@ class GuiProjectDetailsContents(QWidget):
|
||||
|
||||
return
|
||||
|
||||
def getColumnSizes(self):
|
||||
"""Return the column widths for the tree columns.
|
||||
"""
|
||||
def getColumnSizes(self) -> list[int]:
|
||||
"""Return the column widths for the tree columns."""
|
||||
retVals = [
|
||||
self.tocTree.columnWidth(0),
|
||||
self.tocTree.columnWidth(1),
|
||||
@@ -418,24 +416,21 @@ class GuiProjectDetailsContents(QWidget):
|
||||
]
|
||||
return retVals
|
||||
|
||||
def updateValues(self):
|
||||
"""Populate the tree.
|
||||
"""
|
||||
def updateValues(self) -> None:
|
||||
"""Populate the tree."""
|
||||
self._currentRoot = None
|
||||
self.novelValue.updateList()
|
||||
self.novelValue.setHandle(self.novelValue.firstHandle)
|
||||
self._prepareData(self.novelValue.firstHandle)
|
||||
self._populateTree()
|
||||
|
||||
return
|
||||
|
||||
##
|
||||
# Internal Functions
|
||||
##
|
||||
|
||||
def _prepareData(self, rootHandle):
|
||||
"""Extract the information from the project index.
|
||||
"""
|
||||
def _prepareData(self, rootHandle: str | None) -> None:
|
||||
"""Extract the information from the project index."""
|
||||
logger.debug("Populating ToC from handle '%s'", rootHandle)
|
||||
self._theToC = SHARED.project.index.getTableOfContents(rootHandle, 2)
|
||||
self._theToC.append(("", 0, self.tr("END"), 0))
|
||||
@@ -446,9 +441,8 @@ class GuiProjectDetailsContents(QWidget):
|
||||
##
|
||||
|
||||
@pyqtSlot(str)
|
||||
def _novelValueChanged(self, tHandle):
|
||||
"""Refresh the tree with another root item.
|
||||
"""
|
||||
def _novelValueChanged(self, tHandle: str) -> None:
|
||||
"""Refresh the tree with another root item."""
|
||||
if tHandle != self._currentRoot:
|
||||
self._prepareData(tHandle)
|
||||
self._populateTree()
|
||||
@@ -456,9 +450,8 @@ class GuiProjectDetailsContents(QWidget):
|
||||
return
|
||||
|
||||
@pyqtSlot()
|
||||
def _populateTree(self):
|
||||
"""Set the content of the chapter/page tree.
|
||||
"""
|
||||
def _populateTree(self) -> None:
|
||||
"""Set the content of the chapter/page tree."""
|
||||
dblPages = self.dblValue.isChecked()
|
||||
wpPage = self.wpValue.value()
|
||||
fstPage = self.poValue.value() - 1
|
||||
|
||||
@@ -153,7 +153,7 @@ class GuiProjectLoad(QDialog):
|
||||
|
||||
return
|
||||
|
||||
def __del__(self): # pragma: no cover
|
||||
def __del__(self) -> None: # pragma: no cover
|
||||
logger.debug("Delete: GuiProjectLoad")
|
||||
return
|
||||
|
||||
|
||||
@@ -27,11 +27,11 @@ import logging
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from PyQt5.QtGui import QIcon, QPixmap, QColor
|
||||
from PyQt5.QtCore import Qt, pyqtSlot
|
||||
from PyQt5.QtGui import QCloseEvent, QIcon, QPixmap, QColor
|
||||
from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot
|
||||
from PyQt5.QtWidgets import (
|
||||
QColorDialog, QComboBox, QDialogButtonBox, QHBoxLayout, QLabel, QLineEdit,
|
||||
QPushButton, QTreeWidget, QTreeWidgetItem, QVBoxLayout, QWidget
|
||||
QPushButton, QTreeWidget, QTreeWidgetItem, QVBoxLayout, QWidget, qApp
|
||||
)
|
||||
|
||||
from novelwriter import CONFIG, SHARED
|
||||
@@ -53,6 +53,8 @@ class GuiProjectSettings(NPagedDialog):
|
||||
TAB_IMPORT = 2
|
||||
TAB_REPLACE = 3
|
||||
|
||||
newProjectSettingsReady = pyqtSignal()
|
||||
|
||||
def __init__(self, mainGui: GuiMain, focusTab: int = TAB_MAIN) -> None:
|
||||
super().__init__(parent=mainGui)
|
||||
|
||||
@@ -86,7 +88,8 @@ class GuiProjectSettings(NPagedDialog):
|
||||
|
||||
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
|
||||
self.buttonBox.accepted.connect(self._doSave)
|
||||
self.buttonBox.rejected.connect(self._doClose)
|
||||
self.buttonBox.rejected.connect(self.close)
|
||||
self.rejected.connect(self.close)
|
||||
self.addControls(self.buttonBox)
|
||||
|
||||
# Focus Tab
|
||||
@@ -100,6 +103,13 @@ class GuiProjectSettings(NPagedDialog):
|
||||
logger.debug("Delete: GuiProjectSettings")
|
||||
return
|
||||
|
||||
def closeEvent(self, event: QCloseEvent) -> None:
|
||||
"""Capture the close event and perform cleanup."""
|
||||
self._saveGuiSettings()
|
||||
event.accept()
|
||||
self.deleteLater()
|
||||
return
|
||||
|
||||
##
|
||||
# Private Slots
|
||||
##
|
||||
@@ -137,18 +147,12 @@ class GuiProjectSettings(NPagedDialog):
|
||||
newList = self.tabReplace.getNewList()
|
||||
project.data.setAutoReplace(newList)
|
||||
|
||||
self._saveGuiSettings()
|
||||
self.accept()
|
||||
self.newProjectSettingsReady.emit()
|
||||
qApp.processEvents()
|
||||
self.close()
|
||||
|
||||
return
|
||||
|
||||
@pyqtSlot()
|
||||
def _doClose(self) -> None:
|
||||
"""Save settings and close the dialog."""
|
||||
self._saveGuiSettings()
|
||||
self.reject()
|
||||
return
|
||||
|
||||
##
|
||||
# Internal Functions
|
||||
##
|
||||
@@ -173,6 +177,7 @@ class GuiProjectSettings(NPagedDialog):
|
||||
statusColW = CONFIG.rpxInt(self.tabStatus.listBox.columnWidth(0))
|
||||
importColW = CONFIG.rpxInt(self.tabImport.listBox.columnWidth(0))
|
||||
|
||||
logger.debug("Saving State: GuiProjectSettings")
|
||||
pOptions = SHARED.project.options
|
||||
pOptions.setValue("GuiProjectSettings", "winWidth", winWidth)
|
||||
pOptions.setValue("GuiProjectSettings", "winHeight", winHeight)
|
||||
|
||||
@@ -25,11 +25,11 @@ from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from PyQt5.QtGui import QFontMetrics
|
||||
from PyQt5.QtCore import Qt, QSize
|
||||
from PyQt5.QtGui import QCloseEvent, QFontMetrics
|
||||
from PyQt5.QtCore import QSize, Qt, pyqtSlot
|
||||
from PyQt5.QtWidgets import (
|
||||
QLabel, QVBoxLayout, QHBoxLayout, QDialog, QDialogButtonBox,
|
||||
QListWidget, QListWidgetItem, QFrame
|
||||
QDialog, QDialogButtonBox, QFrame, QHBoxLayout, QLabel, QListWidget,
|
||||
QListWidgetItem, QVBoxLayout, QWidget
|
||||
)
|
||||
|
||||
from novelwriter import CONFIG
|
||||
@@ -44,9 +44,12 @@ class GuiQuoteSelect(QDialog):
|
||||
|
||||
D_KEY = Qt.ItemDataRole.UserRole
|
||||
|
||||
def __init__(self, parent=None, currentQuote='"'):
|
||||
def __init__(self, parent: QWidget, currentQuote: str = '"') -> None:
|
||||
super().__init__(parent=parent)
|
||||
|
||||
logger.debug("Create: GuiQuoteSelect")
|
||||
self.setObjectName("GuiQuoteSelect")
|
||||
|
||||
self.outerBox = QVBoxLayout()
|
||||
self.innerBox = QHBoxLayout()
|
||||
self.labelBox = QVBoxLayout()
|
||||
@@ -87,8 +90,8 @@ class GuiQuoteSelect(QDialog):
|
||||
|
||||
# Buttons
|
||||
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
|
||||
self.buttonBox.accepted.connect(self._doAccept)
|
||||
self.buttonBox.rejected.connect(self._doReject)
|
||||
self.buttonBox.accepted.connect(self.accept)
|
||||
self.buttonBox.rejected.connect(self.reject)
|
||||
|
||||
# Assemble
|
||||
self.labelBox.addWidget(self.previewLabel, 0, Qt.AlignTop)
|
||||
@@ -102,15 +105,31 @@ class GuiQuoteSelect(QDialog):
|
||||
|
||||
self.setLayout(self.outerBox)
|
||||
|
||||
logger.debug("Ready: GuiQuoteSelect")
|
||||
|
||||
return
|
||||
|
||||
def __del__(self) -> None: # pragma: no cover
|
||||
logger.debug("Delete: GuiQuoteSelect")
|
||||
return
|
||||
|
||||
##
|
||||
# Slots
|
||||
# Events
|
||||
##
|
||||
|
||||
def _selectedSymbol(self):
|
||||
"""Update the preview label and the selected quote style.
|
||||
"""
|
||||
def closeEvent(self, event: QCloseEvent) -> None:
|
||||
"""Capture the close event and perform cleanup."""
|
||||
event.accept()
|
||||
self.deleteLater()
|
||||
return
|
||||
|
||||
##
|
||||
# Private Slots
|
||||
##
|
||||
|
||||
@pyqtSlot()
|
||||
def _selectedSymbol(self) -> None:
|
||||
"""Update the preview label and the selected quote style."""
|
||||
selItems = self.listBox.selectedItems()
|
||||
if selItems:
|
||||
theSymbol = selItems[0].data(self.D_KEY)
|
||||
@@ -118,16 +137,4 @@ class GuiQuoteSelect(QDialog):
|
||||
self.selectedQuote = theSymbol
|
||||
return
|
||||
|
||||
def _doAccept(self):
|
||||
"""Ok button clicked.
|
||||
"""
|
||||
self.accept()
|
||||
return
|
||||
|
||||
def _doReject(self):
|
||||
"""Cancel button clicked.
|
||||
"""
|
||||
self.reject()
|
||||
return
|
||||
|
||||
# END Class GuiQuoteSelect
|
||||
|
||||
@@ -30,7 +30,7 @@ from datetime import datetime
|
||||
from urllib.request import Request, urlopen
|
||||
|
||||
from PyQt5.QtGui import QCloseEvent, QCursor
|
||||
from PyQt5.QtCore import Qt, pyqtSlot
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtWidgets import (
|
||||
QWidget, qApp, QDialog, QHBoxLayout, QVBoxLayout, QDialogButtonBox, QLabel
|
||||
)
|
||||
@@ -95,7 +95,7 @@ class GuiUpdates(QDialog):
|
||||
|
||||
# Buttons
|
||||
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Close)
|
||||
self.buttonBox.rejected.connect(self._doClose)
|
||||
self.buttonBox.rejected.connect(self.close)
|
||||
|
||||
# Assemble
|
||||
self.innerBox = QHBoxLayout()
|
||||
@@ -169,14 +169,4 @@ class GuiUpdates(QDialog):
|
||||
self.deleteLater()
|
||||
return
|
||||
|
||||
##
|
||||
# Private Slots
|
||||
##
|
||||
|
||||
@pyqtSlot()
|
||||
def _doClose(self) -> None:
|
||||
"""Close the dialog."""
|
||||
self.close()
|
||||
return
|
||||
|
||||
# END Class GuiUpdates
|
||||
|
||||
@@ -27,10 +27,11 @@ import logging
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtGui import QCloseEvent
|
||||
from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot
|
||||
from PyQt5.QtWidgets import (
|
||||
QAbstractItemView, QDialog, QDialogButtonBox, QHBoxLayout, QLabel,
|
||||
QLineEdit, QListWidget, QListWidgetItem, QPushButton, QVBoxLayout
|
||||
QLineEdit, QListWidget, QListWidgetItem, QPushButton, QVBoxLayout, qApp
|
||||
)
|
||||
|
||||
from novelwriter import CONFIG, SHARED
|
||||
@@ -44,7 +45,9 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class GuiWordList(QDialog):
|
||||
|
||||
def __init__(self, mainGui: GuiMain):
|
||||
newWordListReady = pyqtSignal()
|
||||
|
||||
def __init__(self, mainGui: GuiMain) -> None:
|
||||
super().__init__(parent=mainGui)
|
||||
|
||||
logger.debug("Create: GuiWordList")
|
||||
@@ -87,7 +90,7 @@ class GuiWordList(QDialog):
|
||||
|
||||
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Save | QDialogButtonBox.Close)
|
||||
self.buttonBox.accepted.connect(self._doSave)
|
||||
self.buttonBox.rejected.connect(self._doClose)
|
||||
self.buttonBox.rejected.connect(self.close)
|
||||
|
||||
# Assemble
|
||||
# ========
|
||||
@@ -108,15 +111,27 @@ class GuiWordList(QDialog):
|
||||
|
||||
return
|
||||
|
||||
def __del__(self): # pragma: no cover
|
||||
def __del__(self) -> None: # pragma: no cover
|
||||
logger.debug("Delete: GuiWordList")
|
||||
return
|
||||
|
||||
##
|
||||
# Slots
|
||||
# Events
|
||||
##
|
||||
|
||||
def _doAdd(self):
|
||||
def closeEvent(self, event: QCloseEvent) -> None:
|
||||
"""Capture the close event and perform cleanup."""
|
||||
self._saveGuiSettings()
|
||||
event.accept()
|
||||
self.deleteLater()
|
||||
return
|
||||
|
||||
##
|
||||
# Private Slots
|
||||
##
|
||||
|
||||
@pyqtSlot()
|
||||
def _doAdd(self) -> None:
|
||||
"""Add a new word to the word list."""
|
||||
word = self.newEntry.text().strip()
|
||||
if word == "":
|
||||
@@ -134,16 +149,17 @@ class GuiWordList(QDialog):
|
||||
|
||||
return
|
||||
|
||||
def _doDelete(self):
|
||||
@pyqtSlot()
|
||||
def _doDelete(self) -> None:
|
||||
"""Delete the selected item."""
|
||||
selItem = self.listBox.selectedItems()
|
||||
if selItem:
|
||||
self.listBox.takeItem(self.listBox.row(selItem[0]))
|
||||
return
|
||||
|
||||
def _doSave(self):
|
||||
@pyqtSlot()
|
||||
def _doSave(self) -> None:
|
||||
"""Save the new word list and close."""
|
||||
self._saveGuiSettings()
|
||||
userDict = UserDictionary(SHARED.project)
|
||||
for i in range(self.listBox.count()):
|
||||
item = self.listBox.item(i)
|
||||
@@ -152,20 +168,16 @@ class GuiWordList(QDialog):
|
||||
if word:
|
||||
userDict.add(word)
|
||||
userDict.save()
|
||||
self.accept()
|
||||
return True
|
||||
|
||||
def _doClose(self):
|
||||
"""Close without saving the word list."""
|
||||
self._saveGuiSettings()
|
||||
self.reject()
|
||||
self.newWordListReady.emit()
|
||||
qApp.processEvents()
|
||||
self.close()
|
||||
return
|
||||
|
||||
##
|
||||
# Internal Functions
|
||||
##
|
||||
|
||||
def _loadWordList(self):
|
||||
def _loadWordList(self) -> None:
|
||||
"""Load the project's word list, if it exists."""
|
||||
userDict = UserDictionary(SHARED.project)
|
||||
userDict.load()
|
||||
@@ -175,11 +187,12 @@ class GuiWordList(QDialog):
|
||||
self.listBox.addItem(word)
|
||||
return
|
||||
|
||||
def _saveGuiSettings(self):
|
||||
def _saveGuiSettings(self) -> None:
|
||||
"""Save GUI settings."""
|
||||
winWidth = CONFIG.rpxInt(self.width())
|
||||
winHeight = CONFIG.rpxInt(self.height())
|
||||
|
||||
logger.debug("Saving State: GuiWordList")
|
||||
pOptions = SHARED.project.options
|
||||
pOptions.setValue("GuiWordList", "winWidth", winWidth)
|
||||
pOptions.setValue("GuiWordList", "winHeight", winHeight)
|
||||
|
||||
@@ -140,6 +140,7 @@ class nwDocInsert(Enum):
|
||||
NEW_PAGE = 7
|
||||
VSPACE_S = 8
|
||||
VSPACE_M = 9
|
||||
LIPSUM = 10
|
||||
|
||||
# END Enum nwDocInsert
|
||||
|
||||
|
||||
@@ -58,6 +58,7 @@ from novelwriter.common import minmax, transferCase
|
||||
from novelwriter.constants import nwKeyWords, nwLabels, nwShortcode, nwUnicode, trConst
|
||||
from novelwriter.core.item import NWItem
|
||||
from novelwriter.core.index import countWords
|
||||
from novelwriter.tools.lipsum import GuiLipsum
|
||||
from novelwriter.core.document import NWDocument
|
||||
from novelwriter.gui.dochighlight import GuiDocHighlighter
|
||||
from novelwriter.gui.editordocument import GuiTextDocument
|
||||
@@ -850,18 +851,23 @@ class GuiDocEditor(QPlainTextEdit):
|
||||
text = "[vspace:2]"
|
||||
newBlock = True
|
||||
goAfter = False
|
||||
elif insert == nwDocInsert.LIPSUM:
|
||||
text = GuiLipsum.getLipsum(self)
|
||||
newBlock = True
|
||||
goAfter = False
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
if newBlock:
|
||||
self.insertNewBlock(text, defaultAfter=goAfter)
|
||||
else:
|
||||
cursor = self.textCursor()
|
||||
cursor.beginEditBlock()
|
||||
cursor.insertText(text)
|
||||
cursor.endEditBlock()
|
||||
if text:
|
||||
if newBlock:
|
||||
self.insertNewBlock(text, defaultAfter=goAfter)
|
||||
else:
|
||||
cursor = self.textCursor()
|
||||
cursor.beginEditBlock()
|
||||
cursor.insertText(text)
|
||||
cursor.endEditBlock()
|
||||
|
||||
return True
|
||||
|
||||
@@ -1144,6 +1150,7 @@ class GuiDocEditor(QPlainTextEdit):
|
||||
|
||||
# Execute the context menu
|
||||
ctxMenu.exec_(self.viewport().mapToGlobal(pos))
|
||||
ctxMenu.deleteLater()
|
||||
|
||||
return
|
||||
|
||||
|
||||
@@ -378,33 +378,34 @@ class GuiDocViewer(QTextBrowser):
|
||||
userCursor = self.textCursor()
|
||||
userSelection = userCursor.hasSelection()
|
||||
|
||||
mnuContext = QMenu(self)
|
||||
ctxMenu = QMenu(self)
|
||||
|
||||
if userSelection:
|
||||
mnuCopy = QAction(self.tr("Copy"), mnuContext)
|
||||
mnuCopy = QAction(self.tr("Copy"), ctxMenu)
|
||||
mnuCopy.triggered.connect(lambda: self.docAction(nwDocAction.COPY))
|
||||
mnuContext.addAction(mnuCopy)
|
||||
ctxMenu.addAction(mnuCopy)
|
||||
|
||||
mnuContext.addSeparator()
|
||||
ctxMenu.addSeparator()
|
||||
|
||||
mnuSelAll = QAction(self.tr("Select All"), mnuContext)
|
||||
mnuSelAll = QAction(self.tr("Select All"), ctxMenu)
|
||||
mnuSelAll.triggered.connect(lambda: self.docAction(nwDocAction.SEL_ALL))
|
||||
mnuContext.addAction(mnuSelAll)
|
||||
ctxMenu.addAction(mnuSelAll)
|
||||
|
||||
mnuSelWord = QAction(self.tr("Select Word"), mnuContext)
|
||||
mnuSelWord = QAction(self.tr("Select Word"), ctxMenu)
|
||||
mnuSelWord.triggered.connect(
|
||||
lambda: self._makePosSelection(QTextCursor.SelectionType.WordUnderCursor, point)
|
||||
)
|
||||
mnuContext.addAction(mnuSelWord)
|
||||
ctxMenu.addAction(mnuSelWord)
|
||||
|
||||
mnuSelPara = QAction(self.tr("Select Paragraph"), mnuContext)
|
||||
mnuSelPara = QAction(self.tr("Select Paragraph"), ctxMenu)
|
||||
mnuSelPara.triggered.connect(
|
||||
lambda: self._makePosSelection(QTextCursor.SelectionType.BlockUnderCursor, point)
|
||||
)
|
||||
mnuContext.addAction(mnuSelPara)
|
||||
ctxMenu.addAction(mnuSelPara)
|
||||
|
||||
# Open the context menu
|
||||
mnuContext.exec_(self.viewport().mapToGlobal(point))
|
||||
ctxMenu.exec_(self.viewport().mapToGlobal(point))
|
||||
ctxMenu.deleteLater()
|
||||
|
||||
return
|
||||
|
||||
|
||||
@@ -123,6 +123,7 @@ class GuiDocViewerPanel(QWidget):
|
||||
widths = {}
|
||||
for key, tab in self.kwTabs.items():
|
||||
widths[key] = tab.getColumnWidths()
|
||||
logger.debug("Saving State: GuiDocViewerPanel")
|
||||
SHARED.project.options.setValue("GuiDocViewerPanel", "colWidths", widths)
|
||||
return
|
||||
|
||||
|
||||
+12
-10
@@ -154,12 +154,12 @@ class GuiMainMenu(QMenuBar):
|
||||
# Project > Project Settings
|
||||
self.aProjectSettings = self.projMenu.addAction(self.tr("Project Settings"))
|
||||
self.aProjectSettings.setShortcut("Ctrl+Shift+,")
|
||||
self.aProjectSettings.triggered.connect(lambda: self.mainGui.showProjectSettingsDialog())
|
||||
self.aProjectSettings.triggered.connect(self.mainGui.showProjectSettingsDialog)
|
||||
|
||||
# Project > Project Details
|
||||
self.aProjectDetails = self.projMenu.addAction(self.tr("Project Details"))
|
||||
self.aProjectDetails.setShortcut("Shift+F6")
|
||||
self.aProjectDetails.triggered.connect(lambda: self.mainGui.showProjectDetailsDialog())
|
||||
self.aProjectDetails.triggered.connect(self.mainGui.showProjectDetailsDialog)
|
||||
|
||||
# Project > Separator
|
||||
self.projMenu.addSeparator()
|
||||
@@ -593,8 +593,10 @@ class GuiMainMenu(QMenuBar):
|
||||
)
|
||||
|
||||
# Insert > Placeholder Text
|
||||
self.aLipsumText = self.mInsBreaks.addAction(self.tr("Placeholder Text"))
|
||||
self.aLipsumText.triggered.connect(lambda: self.mainGui.showLoremIpsumDialog())
|
||||
self.aLipsumText = self.insMenu.addAction(self.tr("Placeholder Text"))
|
||||
self.aLipsumText.triggered.connect(
|
||||
lambda: self.requestDocInsert.emit(nwDocInsert.LIPSUM)
|
||||
)
|
||||
|
||||
return
|
||||
|
||||
@@ -872,7 +874,7 @@ class GuiMainMenu(QMenuBar):
|
||||
|
||||
# Tools > Project Word List
|
||||
self.aEditWordList = self.toolsMenu.addAction(self.tr("Project Word List"))
|
||||
self.aEditWordList.triggered.connect(lambda: self.mainGui.showProjectWordListDialog())
|
||||
self.aEditWordList.triggered.connect(self.mainGui.showProjectWordListDialog)
|
||||
|
||||
# Tools > Add Dictionaries
|
||||
if CONFIG.osWindows or CONFIG.isDebug:
|
||||
@@ -902,13 +904,13 @@ class GuiMainMenu(QMenuBar):
|
||||
# Tools > Writing Statistics
|
||||
self.aWritingStats = self.toolsMenu.addAction(self.tr("Writing Statistics"))
|
||||
self.aWritingStats.setShortcut("F6")
|
||||
self.aWritingStats.triggered.connect(lambda: self.mainGui.showWritingStatsDialog())
|
||||
self.aWritingStats.triggered.connect(self.mainGui.showWritingStatsDialog)
|
||||
|
||||
# Tools > Preferences
|
||||
self.aPreferences = self.toolsMenu.addAction(self.tr("Preferences"))
|
||||
self.aPreferences.setShortcut("Ctrl+,")
|
||||
self.aPreferences.setMenuRole(QAction.PreferencesRole)
|
||||
self.aPreferences.triggered.connect(lambda: self.mainGui.showPreferencesDialog())
|
||||
self.aPreferences.triggered.connect(self.mainGui.showPreferencesDialog)
|
||||
|
||||
return
|
||||
|
||||
@@ -920,12 +922,12 @@ class GuiMainMenu(QMenuBar):
|
||||
# Help > About
|
||||
self.aAboutNW = self.helpMenu.addAction(self.tr("About novelWriter"))
|
||||
self.aAboutNW.setMenuRole(QAction.AboutRole)
|
||||
self.aAboutNW.triggered.connect(lambda: self.mainGui.showAboutNWDialog())
|
||||
self.aAboutNW.triggered.connect(self.mainGui.showAboutNWDialog)
|
||||
|
||||
# Help > About Qt5
|
||||
self.aAboutQt = self.helpMenu.addAction(self.tr("About Qt5"))
|
||||
self.aAboutQt.setMenuRole(QAction.AboutQtRole)
|
||||
self.aAboutQt.triggered.connect(lambda: self.mainGui.showAboutQtDialog())
|
||||
self.aAboutQt.triggered.connect(self.mainGui.showAboutQtDialog)
|
||||
|
||||
# Help > Separator
|
||||
self.helpMenu.addSeparator()
|
||||
@@ -961,7 +963,7 @@ class GuiMainMenu(QMenuBar):
|
||||
|
||||
# Document > Check for Updates
|
||||
self.aUpdates = self.helpMenu.addAction(self.tr("Check for New Release"))
|
||||
self.aUpdates.triggered.connect(lambda: self.mainGui.showUpdatesDialog())
|
||||
self.aUpdates.triggered.connect(self.mainGui.showUpdatesDialog)
|
||||
|
||||
return
|
||||
|
||||
|
||||
@@ -145,6 +145,7 @@ class GuiNovelView(QWidget):
|
||||
"""Run closing project tasks."""
|
||||
lastColType = self.novelTree.lastColType
|
||||
lastColSize = self.novelTree.lastColSize
|
||||
logger.debug("Saving State: GuiNovelView")
|
||||
pOptions = SHARED.project.options
|
||||
pOptions.setValue("GuiNovelView", "lastCol", lastColType)
|
||||
pOptions.setValue("GuiNovelView", "lastColSize", lastColSize)
|
||||
|
||||
@@ -606,6 +606,7 @@ class GuiOutlineTree(QTreeWidget):
|
||||
logHidden, orgWidth if logHidden and logWidth == 0 else logWidth
|
||||
]
|
||||
|
||||
logger.debug("Saving State: GuiOutline")
|
||||
pOptions = SHARED.project.options
|
||||
pOptions.setValue("GuiOutline", "columnState", colState)
|
||||
pOptions.saveSettings()
|
||||
|
||||
@@ -27,6 +27,7 @@ import logging
|
||||
|
||||
from time import time
|
||||
from typing import TYPE_CHECKING, Literal
|
||||
from datetime import datetime
|
||||
|
||||
from PyQt5.QtCore import pyqtSlot, QLocale
|
||||
from PyQt5.QtGui import QColor
|
||||
@@ -50,8 +51,9 @@ class GuiMainStatus(QStatusBar):
|
||||
|
||||
logger.debug("Create: GuiMainStatus")
|
||||
|
||||
self._refTime = -1.0
|
||||
self._refTime = -1.0
|
||||
self._userIdle = False
|
||||
self._debugInfo = False
|
||||
|
||||
colNone = QColor(*SHARED.theme.statNone)
|
||||
colSaved = QColor(*SHARED.theme.statSaved)
|
||||
@@ -223,4 +225,41 @@ class GuiMainStatus(QStatusBar):
|
||||
self.setDocumentStatus(StatusLED.S_BAD if status else StatusLED.S_GOOD)
|
||||
return
|
||||
|
||||
##
|
||||
# Debug
|
||||
##
|
||||
|
||||
def memInfo(self) -> None: # pragma: no cover
|
||||
"""Display memory info on the status bar. This is used to
|
||||
investigate memory usage and Qt widgets that get left in memory.
|
||||
Enabled by the --meminfo command line flag.
|
||||
"""
|
||||
import tracemalloc
|
||||
from collections import Counter
|
||||
|
||||
widgets = qApp.allWidgets()
|
||||
if not self._debugInfo:
|
||||
if tracemalloc.is_tracing():
|
||||
self._traceMallocRef = "Total"
|
||||
else:
|
||||
self._traceMallocRef = "Relative"
|
||||
tracemalloc.start()
|
||||
self._debugInfo = True
|
||||
self._wCounts = Counter([type(x).__name__ for x in widgets])
|
||||
|
||||
if hasattr(self, "_wCounts"):
|
||||
diff = Counter([type(x).__name__ for x in widgets]) - self._wCounts
|
||||
for name, count in diff.items():
|
||||
logger.debug("Widget '%s': +%d", name, count)
|
||||
|
||||
mem = tracemalloc.get_traced_memory()
|
||||
stamp = datetime.now().strftime("%H:%M:%S")
|
||||
self.showMessage((
|
||||
f"Debug [{stamp}]"
|
||||
f" \u2013 Widgets: {len(qApp.allWidgets())}"
|
||||
f" \u2013 {self._traceMallocRef} Memory: {mem[0]:n}"
|
||||
f" \u2013 Peak: {mem[1]:n}"
|
||||
), 6000)
|
||||
return
|
||||
|
||||
# END Class GuiMainStatus
|
||||
|
||||
+135
-177
@@ -56,7 +56,6 @@ from novelwriter.dialogs.wordlist import GuiWordList
|
||||
from novelwriter.dialogs.preferences import GuiPreferences
|
||||
from novelwriter.dialogs.projdetails import GuiProjectDetails
|
||||
from novelwriter.dialogs.projsettings import GuiProjectSettings
|
||||
from novelwriter.tools.lipsum import GuiLipsum
|
||||
from novelwriter.tools.manuscript import GuiManuscript
|
||||
from novelwriter.tools.projwizard import GuiProjectWizard
|
||||
from novelwriter.tools.dictionaries import GuiDictionaries
|
||||
@@ -66,7 +65,7 @@ from novelwriter.core.coretools import ProjectBuilder
|
||||
from novelwriter.enum import (
|
||||
nwDocAction, nwDocInsert, nwDocMode, nwItemType, nwItemClass, nwWidget, nwView
|
||||
)
|
||||
from novelwriter.common import getGuiItem, hexToInt
|
||||
from novelwriter.common import hexToInt
|
||||
from novelwriter.constants import nwFiles
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -866,213 +865,111 @@ class GuiMain(QMainWindow):
|
||||
|
||||
return None
|
||||
|
||||
@pyqtSlot()
|
||||
def showPreferencesDialog(self) -> None:
|
||||
"""Open the preferences dialog."""
|
||||
dlgConf = GuiPreferences(self)
|
||||
dlgConf.exec_()
|
||||
|
||||
if dlgConf.result() == QDialog.Accepted:
|
||||
logger.debug("Applying new preferences")
|
||||
self.initMain()
|
||||
self.saveDocument()
|
||||
|
||||
if dlgConf.needsRestart:
|
||||
SHARED.info(self.tr(
|
||||
"Some changes will not be applied until novelWriter has been restarted."
|
||||
))
|
||||
|
||||
if dlgConf.refreshTree:
|
||||
self.projView.populateTree()
|
||||
|
||||
if dlgConf.updateTheme:
|
||||
# We are doing this manually instead of connecting to
|
||||
# qApp.paletteChanged since the processing order matters
|
||||
SHARED.theme.loadTheme()
|
||||
self.docEditor.updateTheme()
|
||||
self.docViewer.updateTheme()
|
||||
self.docViewerPanel.updateTheme()
|
||||
self.sideBar.updateTheme()
|
||||
self.projView.updateTheme()
|
||||
self.novelView.updateTheme()
|
||||
self.outlineView.updateTheme()
|
||||
self.itemDetails.updateTheme()
|
||||
self.mainStatus.updateTheme()
|
||||
|
||||
if dlgConf.updateSyntax:
|
||||
SHARED.theme.loadSyntax()
|
||||
self.docEditor.updateSyntaxColours()
|
||||
|
||||
self.docEditor.initEditor()
|
||||
self.docViewer.initViewer()
|
||||
self.projView.initSettings()
|
||||
self.novelView.initSettings()
|
||||
self.outlineView.initSettings()
|
||||
|
||||
self._updateStatusWordCount()
|
||||
|
||||
dialog = GuiPreferences(self)
|
||||
dialog.newPreferencesReady.connect(self._processConfigChanges)
|
||||
dialog.exec_()
|
||||
return
|
||||
|
||||
@pyqtSlot()
|
||||
@pyqtSlot(int)
|
||||
def showProjectSettingsDialog(self, focusTab: int = GuiProjectSettings.TAB_MAIN) -> bool:
|
||||
def showProjectSettingsDialog(self, focusTab: int = GuiProjectSettings.TAB_MAIN) -> None:
|
||||
"""Open the project settings dialog."""
|
||||
if not SHARED.hasProject:
|
||||
logger.error("No project open")
|
||||
return False
|
||||
|
||||
dlgProj = GuiProjectSettings(self, focusTab=focusTab)
|
||||
dlgProj.exec_()
|
||||
|
||||
if dlgProj.result() == QDialog.Accepted:
|
||||
logger.debug("Applying new project settings")
|
||||
SHARED.updateSpellCheckLanguage()
|
||||
self.itemDetails.refreshDetails()
|
||||
self._updateWindowTitle(SHARED.project.data.name)
|
||||
|
||||
return True
|
||||
|
||||
def showProjectDetailsDialog(self) -> bool:
|
||||
"""Open the project details dialog."""
|
||||
if not SHARED.hasProject:
|
||||
logger.error("No project open")
|
||||
return False
|
||||
|
||||
dlgDetails = getGuiItem("GuiProjectDetails")
|
||||
if dlgDetails is None:
|
||||
dlgDetails = GuiProjectDetails(self)
|
||||
assert isinstance(dlgDetails, GuiProjectDetails)
|
||||
|
||||
dlgDetails.setModal(True)
|
||||
dlgDetails.show()
|
||||
dlgDetails.raise_()
|
||||
dlgDetails.updateValues()
|
||||
|
||||
return True
|
||||
if SHARED.hasProject:
|
||||
dialog = GuiProjectSettings(self, focusTab=focusTab)
|
||||
dialog.newProjectSettingsReady.connect(self._processProjectSettingsChanges)
|
||||
dialog.exec_()
|
||||
return
|
||||
|
||||
@pyqtSlot()
|
||||
def showBuildManuscriptDialog(self) -> bool:
|
||||
def showProjectDetailsDialog(self) -> None:
|
||||
"""Open the project details dialog."""
|
||||
if SHARED.hasProject:
|
||||
dialog = GuiProjectDetails(self)
|
||||
dialog.setModal(True)
|
||||
dialog.show()
|
||||
dialog.raise_()
|
||||
qApp.processEvents()
|
||||
dialog.updateValues()
|
||||
return
|
||||
|
||||
@pyqtSlot()
|
||||
def showBuildManuscriptDialog(self) -> None:
|
||||
"""Open the build manuscript dialog."""
|
||||
if not SHARED.hasProject:
|
||||
logger.error("No project open")
|
||||
return False
|
||||
if SHARED.hasProject:
|
||||
dialog = GuiManuscript(self)
|
||||
dialog.setModal(False)
|
||||
dialog.show()
|
||||
dialog.raise_()
|
||||
qApp.processEvents()
|
||||
dialog.loadContent()
|
||||
return
|
||||
|
||||
dlgBuild = getGuiItem("GuiManuscript")
|
||||
if dlgBuild is None:
|
||||
dlgBuild = GuiManuscript(self)
|
||||
assert isinstance(dlgBuild, GuiManuscript)
|
||||
|
||||
dlgBuild.setModal(False)
|
||||
dlgBuild.show()
|
||||
dlgBuild.raise_()
|
||||
qApp.processEvents()
|
||||
|
||||
dlgBuild.loadContent()
|
||||
|
||||
return True
|
||||
|
||||
def showLoremIpsumDialog(self) -> bool:
|
||||
"""Open the insert lorem ipsum text dialog."""
|
||||
if not SHARED.hasProject:
|
||||
logger.error("No project open")
|
||||
return False
|
||||
|
||||
dlgLipsum = getGuiItem("GuiLipsum")
|
||||
if dlgLipsum is None:
|
||||
dlgLipsum = GuiLipsum(self)
|
||||
assert isinstance(dlgLipsum, GuiLipsum)
|
||||
|
||||
dlgLipsum.setModal(False)
|
||||
dlgLipsum.show()
|
||||
dlgLipsum.raise_()
|
||||
qApp.processEvents()
|
||||
|
||||
return True
|
||||
|
||||
def showProjectWordListDialog(self) -> bool:
|
||||
@pyqtSlot()
|
||||
def showProjectWordListDialog(self) -> None:
|
||||
"""Open the project word list dialog."""
|
||||
if not SHARED.hasProject:
|
||||
logger.error("No project open")
|
||||
return False
|
||||
if SHARED.hasProject:
|
||||
dialog = GuiWordList(self)
|
||||
dialog.newWordListReady.connect(self._processWordListChanges)
|
||||
dialog.exec_()
|
||||
return
|
||||
|
||||
dlgWords = GuiWordList(self)
|
||||
dlgWords.exec_()
|
||||
|
||||
if dlgWords.result() == QDialog.Accepted:
|
||||
logger.debug("Reloading word list")
|
||||
SHARED.updateSpellCheckLanguage(reload=True)
|
||||
self.docEditor.spellCheckDocument()
|
||||
|
||||
return True
|
||||
|
||||
def showWritingStatsDialog(self) -> bool:
|
||||
@pyqtSlot()
|
||||
def showWritingStatsDialog(self) -> None:
|
||||
"""Open the session stats dialog."""
|
||||
if not SHARED.hasProject:
|
||||
logger.error("No project open")
|
||||
return False
|
||||
if SHARED.hasProject:
|
||||
dialog = GuiWritingStats(self)
|
||||
dialog.setModal(False)
|
||||
dialog.show()
|
||||
dialog.raise_()
|
||||
qApp.processEvents()
|
||||
dialog.populateGUI()
|
||||
return
|
||||
|
||||
dlgStats = getGuiItem("GuiWritingStats")
|
||||
if dlgStats is None:
|
||||
dlgStats = GuiWritingStats(self)
|
||||
assert isinstance(dlgStats, GuiWritingStats)
|
||||
|
||||
dlgStats.setModal(False)
|
||||
dlgStats.show()
|
||||
dlgStats.raise_()
|
||||
@pyqtSlot()
|
||||
def showAboutNWDialog(self, showNotes: bool = False) -> None:
|
||||
"""Show the novelWriter about dialog."""
|
||||
dialog = GuiAbout(self)
|
||||
dialog.setModal(True)
|
||||
dialog.show()
|
||||
dialog.raise_()
|
||||
qApp.processEvents()
|
||||
dlgStats.populateGUI()
|
||||
|
||||
return True
|
||||
|
||||
def showAboutNWDialog(self, showNotes: bool = False) -> bool:
|
||||
"""Show the about dialog for novelWriter."""
|
||||
dlgAbout = getGuiItem("GuiAbout")
|
||||
if dlgAbout is None:
|
||||
dlgAbout = GuiAbout(self)
|
||||
assert isinstance(dlgAbout, GuiAbout)
|
||||
|
||||
dlgAbout.setModal(True)
|
||||
dlgAbout.show()
|
||||
dlgAbout.raise_()
|
||||
qApp.processEvents()
|
||||
dlgAbout.populateGUI()
|
||||
|
||||
dialog.populateGUI()
|
||||
if showNotes:
|
||||
dlgAbout.showReleaseNotes()
|
||||
|
||||
return True
|
||||
dialog.showReleaseNotes()
|
||||
return
|
||||
|
||||
@pyqtSlot()
|
||||
def showAboutQtDialog(self) -> None:
|
||||
"""Show the about dialog for Qt."""
|
||||
"""Show the Qt about dialog."""
|
||||
msgBox = QMessageBox(self)
|
||||
msgBox.aboutQt(self, "About Qt")
|
||||
return
|
||||
|
||||
@pyqtSlot()
|
||||
def showUpdatesDialog(self) -> None:
|
||||
"""Show the check for updates dialog."""
|
||||
dlgUpdate = getGuiItem("GuiUpdates")
|
||||
if dlgUpdate is None:
|
||||
dlgUpdate = GuiUpdates(self)
|
||||
assert isinstance(dlgUpdate, GuiUpdates)
|
||||
|
||||
dlgUpdate.setModal(True)
|
||||
dlgUpdate.show()
|
||||
dlgUpdate.raise_()
|
||||
dialog = GuiUpdates(self)
|
||||
dialog.setModal(True)
|
||||
dialog.show()
|
||||
dialog.raise_()
|
||||
qApp.processEvents()
|
||||
dlgUpdate.checkLatest()
|
||||
|
||||
dialog.checkLatest()
|
||||
return
|
||||
|
||||
@pyqtSlot()
|
||||
def showDictionariesDialog(self) -> None:
|
||||
"""Show the download dictionaries dialog."""
|
||||
dlgDicts = GuiDictionaries(self)
|
||||
dlgDicts.setModal(True)
|
||||
dlgDicts.show()
|
||||
dlgDicts.raise_()
|
||||
dialog = GuiDictionaries(self)
|
||||
dialog.setModal(True)
|
||||
dialog.show()
|
||||
dialog.raise_()
|
||||
qApp.processEvents()
|
||||
if not dlgDicts.initDialog():
|
||||
dlgDicts.close()
|
||||
if not dialog.initDialog():
|
||||
dialog.close()
|
||||
SHARED.error(self.tr("Could not initialise the dialog."))
|
||||
|
||||
return
|
||||
|
||||
def reportConfErr(self) -> bool:
|
||||
@@ -1238,6 +1135,65 @@ class GuiMain(QMainWindow):
|
||||
# Private Slots
|
||||
##
|
||||
|
||||
@pyqtSlot(bool, bool, bool, bool)
|
||||
def _processConfigChanges(self, restart: bool, tree: bool, theme: bool, syntax: bool) -> None:
|
||||
"""Refresh GUI based on flags from the Preferences dialog."""
|
||||
logger.debug("Applying new preferences")
|
||||
self.initMain()
|
||||
self.saveDocument()
|
||||
|
||||
if restart:
|
||||
SHARED.info(self.tr(
|
||||
"Some changes will not be applied until novelWriter has been restarted."
|
||||
))
|
||||
|
||||
if tree:
|
||||
self.projView.populateTree()
|
||||
|
||||
if theme:
|
||||
# We are doing this manually instead of connecting to
|
||||
# qApp.paletteChanged since the processing order matters
|
||||
SHARED.theme.loadTheme()
|
||||
self.docEditor.updateTheme()
|
||||
self.docViewer.updateTheme()
|
||||
self.docViewerPanel.updateTheme()
|
||||
self.sideBar.updateTheme()
|
||||
self.projView.updateTheme()
|
||||
self.novelView.updateTheme()
|
||||
self.outlineView.updateTheme()
|
||||
self.itemDetails.updateTheme()
|
||||
self.mainStatus.updateTheme()
|
||||
|
||||
if syntax:
|
||||
SHARED.theme.loadSyntax()
|
||||
self.docEditor.updateSyntaxColours()
|
||||
|
||||
self.docEditor.initEditor()
|
||||
self.docViewer.initViewer()
|
||||
self.projView.initSettings()
|
||||
self.novelView.initSettings()
|
||||
self.outlineView.initSettings()
|
||||
self._updateStatusWordCount()
|
||||
|
||||
return
|
||||
|
||||
@pyqtSlot()
|
||||
def _processProjectSettingsChanges(self) -> None:
|
||||
"""Refresh data dependent on project settings."""
|
||||
logger.debug("Applying new project settings")
|
||||
SHARED.updateSpellCheckLanguage()
|
||||
self.itemDetails.refreshDetails()
|
||||
self._updateWindowTitle(SHARED.project.data.name)
|
||||
return
|
||||
|
||||
@pyqtSlot()
|
||||
def _processWordListChanges(self) -> None:
|
||||
"""Reload project word list."""
|
||||
logger.debug("Reloading word list")
|
||||
SHARED.updateSpellCheckLanguage(reload=True)
|
||||
self.docEditor.spellCheckDocument()
|
||||
return
|
||||
|
||||
@pyqtSlot(str, nwDocMode)
|
||||
def _followTag(self, tag: str, mode: nwDocMode) -> None:
|
||||
"""Follow a tag after user interaction with a link."""
|
||||
@@ -1321,6 +1277,8 @@ class GuiMain(QMainWindow):
|
||||
self.mainStatus.setUserIdle(editIdle or userIdle)
|
||||
SHARED.updateIdleTime(currTime, editIdle or userIdle)
|
||||
self.mainStatus.updateTime(idleTime=SHARED.projectIdleTime)
|
||||
if CONFIG.memInfo and int(currTime) % 5 == 0: # pragma: no cover
|
||||
self.mainStatus.memInfo()
|
||||
return
|
||||
|
||||
@pyqtSlot()
|
||||
|
||||
+32
-26
@@ -26,10 +26,10 @@ from __future__ import annotations
|
||||
import random
|
||||
import logging
|
||||
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtCore import Qt, pyqtSlot
|
||||
from PyQt5.QtWidgets import (
|
||||
QDialog, QGridLayout, QHBoxLayout, QVBoxLayout, QLabel, QDialogButtonBox,
|
||||
QSpinBox
|
||||
QDialog, QDialogButtonBox, QGridLayout, QHBoxLayout, QLabel, QSpinBox,
|
||||
QVBoxLayout, QWidget
|
||||
)
|
||||
|
||||
from novelwriter import CONFIG, SHARED
|
||||
@@ -41,15 +41,15 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class GuiLipsum(QDialog):
|
||||
|
||||
def __init__(self, mainGui):
|
||||
super().__init__(parent=mainGui)
|
||||
def __init__(self, parent: QWidget) -> None:
|
||||
super().__init__(parent=parent)
|
||||
|
||||
logger.debug("Create: GuiLipsum")
|
||||
self.setObjectName("GuiLipsum")
|
||||
if CONFIG.osDarwin:
|
||||
self.setWindowFlag(Qt.WindowType.Tool)
|
||||
|
||||
self.mainGui = mainGui
|
||||
self._lipsumText = ""
|
||||
|
||||
self.setWindowTitle(self.tr("Insert Placeholder Text"))
|
||||
|
||||
@@ -92,14 +92,16 @@ class GuiLipsum(QDialog):
|
||||
|
||||
# Buttons
|
||||
self.buttonBox = QDialogButtonBox()
|
||||
self.buttonBox.rejected.connect(self._doClose)
|
||||
self.buttonBox.rejected.connect(self.close)
|
||||
|
||||
self.btnClose = self.buttonBox.addButton(QDialogButtonBox.Close)
|
||||
self.btnClose.setAutoDefault(False)
|
||||
|
||||
self.btnSave = self.buttonBox.addButton(self.tr("Insert"), QDialogButtonBox.ActionRole)
|
||||
self.btnSave.clicked.connect(self._doInsert)
|
||||
self.btnSave.setAutoDefault(False)
|
||||
self.btnInsert = self.buttonBox.addButton(self.tr("Insert"), QDialogButtonBox.ActionRole)
|
||||
self.btnInsert.clicked.connect(self._doInsert)
|
||||
self.btnInsert.setAutoDefault(False)
|
||||
|
||||
self.rejected.connect(self.close)
|
||||
|
||||
# Assemble
|
||||
self.outerBox = QVBoxLayout()
|
||||
@@ -112,33 +114,37 @@ class GuiLipsum(QDialog):
|
||||
|
||||
return
|
||||
|
||||
def __del__(self): # pragma: no cover
|
||||
def __del__(self) -> None: # pragma: no cover
|
||||
logger.debug("Delete: GuiLipsum")
|
||||
return
|
||||
|
||||
@property
|
||||
def lipsumText(self) -> str:
|
||||
"""Return the generated text."""
|
||||
return self._lipsumText
|
||||
|
||||
@classmethod
|
||||
def getLipsum(cls, parent: QWidget) -> str:
|
||||
"""Pop the dialog and return the lipsum text."""
|
||||
cls = GuiLipsum(parent)
|
||||
cls.exec_()
|
||||
text = cls.lipsumText
|
||||
cls.deleteLater()
|
||||
return text
|
||||
|
||||
##
|
||||
# Slots
|
||||
# Private Slots
|
||||
##
|
||||
|
||||
def _doInsert(self):
|
||||
"""Load the text and insert it in the open document.
|
||||
"""
|
||||
@pyqtSlot()
|
||||
def _doInsert(self) -> None:
|
||||
"""Generate the text."""
|
||||
lipsumFile = CONFIG.assetPath("text") / "lipsum.txt"
|
||||
lipsumText = readTextFile(lipsumFile).splitlines()
|
||||
|
||||
if self.randSwitch.isChecked():
|
||||
random.shuffle(lipsumText)
|
||||
|
||||
pCount = self.paraCount.value()
|
||||
inText = "\n\n".join(lipsumText[0:pCount]) + "\n\n"
|
||||
|
||||
self.mainGui.docEditor.insertText(inText)
|
||||
|
||||
return
|
||||
|
||||
def _doClose(self):
|
||||
"""Close the dialog window without doing anything.
|
||||
"""
|
||||
self._lipsumText = "\n\n".join(lipsumText[0:pCount]) + "\n\n"
|
||||
self.close()
|
||||
return
|
||||
|
||||
|
||||
@@ -344,8 +344,6 @@ class GuiManuscriptBuild(QDialog):
|
||||
|
||||
def _saveSettings(self):
|
||||
"""Save the user GUI settings."""
|
||||
logger.debug("Saving GuiManuscriptBuild settings")
|
||||
|
||||
winWidth = CONFIG.rpxInt(self.width())
|
||||
winHeight = CONFIG.rpxInt(self.height())
|
||||
|
||||
@@ -353,6 +351,7 @@ class GuiManuscriptBuild(QDialog):
|
||||
fmtWidth = CONFIG.rpxInt(mainSplit[0])
|
||||
sumWidth = CONFIG.rpxInt(mainSplit[1])
|
||||
|
||||
logger.debug("Saving State: GuiManuscriptBuild")
|
||||
pOptions = SHARED.project.options
|
||||
pOptions.setValue("GuiManuscriptBuild", "winWidth", winWidth)
|
||||
pOptions.setValue("GuiManuscriptBuild", "winHeight", winHeight)
|
||||
|
||||
@@ -417,8 +417,6 @@ class GuiManuscript(QDialog):
|
||||
|
||||
def _saveSettings(self):
|
||||
"""Save the user GUI settings."""
|
||||
logger.debug("Saving GuiManuscript settings")
|
||||
|
||||
buildOrder = []
|
||||
for i in range(self.buildList.count()):
|
||||
if item := self.buildList.item(i):
|
||||
@@ -442,6 +440,7 @@ class GuiManuscript(QDialog):
|
||||
detailsWidth = CONFIG.rpxInt(self.buildDetails.getColumnWidth())
|
||||
detailsExpanded = self.buildDetails.getExpandedState()
|
||||
|
||||
logger.debug("Saving State: GuiManuscript")
|
||||
pOptions = SHARED.project.options
|
||||
pOptions.setValue("GuiManuscript", "winWidth", winWidth)
|
||||
pOptions.setValue("GuiManuscript", "winHeight", winHeight)
|
||||
|
||||
@@ -253,13 +253,11 @@ class GuiBuildSettings(QDialog):
|
||||
|
||||
def _saveSettings(self) -> None:
|
||||
"""Save the various user settings."""
|
||||
logger.debug("Saving GuiBuildSettings settings")
|
||||
|
||||
winWidth = CONFIG.rpxInt(self.width())
|
||||
winHeight = CONFIG.rpxInt(self.height())
|
||||
|
||||
treeWidth, filterWidth = self.optTabSelect.mainSplitSizes()
|
||||
|
||||
logger.debug("Saving State: GuiBuildSettings")
|
||||
pOptions = SHARED.project.options
|
||||
pOptions.setValue("GuiBuildSettings", "winWidth", winWidth)
|
||||
pOptions.setValue("GuiBuildSettings", "winHeight", winHeight)
|
||||
|
||||
@@ -29,7 +29,7 @@ import logging
|
||||
from datetime import datetime
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from PyQt5.QtGui import QPixmap, QCursor
|
||||
from PyQt5.QtGui import QCloseEvent, QPixmap, QCursor
|
||||
from PyQt5.QtCore import Qt, pyqtSlot
|
||||
from PyQt5.QtWidgets import (
|
||||
qApp, QDialog, QTreeWidget, QTreeWidgetItem, QDialogButtonBox, QGridLayout,
|
||||
@@ -307,9 +307,20 @@ class GuiWritingStats(QDialog):
|
||||
return
|
||||
|
||||
##
|
||||
# Slots
|
||||
# Events
|
||||
##
|
||||
|
||||
def closeEvent(self, event: QCloseEvent) -> None:
|
||||
"""Capture the user closing the window."""
|
||||
event.accept()
|
||||
self.deleteLater()
|
||||
return
|
||||
|
||||
##
|
||||
# Private Slots
|
||||
##
|
||||
|
||||
@pyqtSlot()
|
||||
def _doClose(self) -> None:
|
||||
"""Save the state of the window, clear cache, end close."""
|
||||
self.logData = []
|
||||
@@ -330,6 +341,7 @@ class GuiWritingStats(QDialog):
|
||||
showIdleTime = self.showIdleTime.isChecked()
|
||||
histMax = self.histMax.value()
|
||||
|
||||
logger.debug("Saving State: GuiWritingStats")
|
||||
pOptions = SHARED.project.options
|
||||
pOptions.setValue("GuiWritingStats", "winWidth", winWidth)
|
||||
pOptions.setValue("GuiWritingStats", "winHeight", winHeight)
|
||||
@@ -347,6 +359,7 @@ class GuiWritingStats(QDialog):
|
||||
pOptions.setValue("GuiWritingStats", "showIdleTime", showIdleTime)
|
||||
pOptions.setValue("GuiWritingStats", "histMax", histMax)
|
||||
pOptions.saveSettings()
|
||||
|
||||
self.close()
|
||||
|
||||
return
|
||||
|
||||
@@ -83,7 +83,7 @@ def testBaseInit_Options(monkeypatch, fncPath):
|
||||
"""Test command line options for logging level."""
|
||||
monkeypatch.setattr("novelwriter.guimain.GuiMain", MockGuiMain)
|
||||
monkeypatch.setattr(sys, "argv", [
|
||||
"novelWriter.py", "--testmode", f"--config={fncPath}", f"--data={fncPath}"
|
||||
"novelWriter.py", "--testmode", "--meminfo", f"--config={fncPath}", f"--data={fncPath}"
|
||||
])
|
||||
|
||||
# Defaults w/None Args
|
||||
|
||||
@@ -35,7 +35,7 @@ from novelwriter.dialogs.about import GuiAbout
|
||||
def testDlgAbout_NWDialog(qtbot, monkeypatch, nwGUI):
|
||||
"""Test the novelWriter about dialogs."""
|
||||
# NW About
|
||||
assert nwGUI.showAboutNWDialog(showNotes=True) is True
|
||||
nwGUI.showAboutNWDialog(showNotes=True)
|
||||
|
||||
qtbot.waitUntil(lambda: getGuiItem("GuiAbout") is not None, timeout=1000)
|
||||
msgAbout = getGuiItem("GuiAbout")
|
||||
@@ -56,14 +56,7 @@ def testDlgAbout_NWDialog(qtbot, monkeypatch, nwGUI):
|
||||
|
||||
msgAbout.showReleaseNotes()
|
||||
assert msgAbout.tabBox.currentWidget() == msgAbout.pageNotes
|
||||
msgAbout._doClose()
|
||||
|
||||
# Open Again from Menu
|
||||
nwGUI.mainMenu.aAboutNW.activate(QAction.Trigger)
|
||||
qtbot.waitUntil(lambda: getGuiItem("GuiAbout") is not None, timeout=1000)
|
||||
msgAbout = getGuiItem("GuiAbout")
|
||||
assert msgAbout is not None
|
||||
msgAbout._doClose()
|
||||
msgAbout.close()
|
||||
|
||||
# END Test testDlgAbout_NWDialog
|
||||
|
||||
|
||||
@@ -45,12 +45,11 @@ def testDlgOther_QuoteSelect(qtbot, nwGUI):
|
||||
lastItem = anItem.text()[2]
|
||||
assert nwQuot.previewLabel.text() == lastItem
|
||||
|
||||
nwQuot._doAccept()
|
||||
nwQuot.accept()
|
||||
assert nwQuot.result() == QDialog.Accepted
|
||||
assert nwQuot.selectedQuote == lastItem
|
||||
|
||||
# qtbot.stop()
|
||||
nwQuot._doReject()
|
||||
nwQuot.close()
|
||||
|
||||
# END Test testDlgOther_QuoteSelect
|
||||
@@ -89,7 +88,7 @@ def testDlgOther_Updates(qtbot, monkeypatch, nwGUI):
|
||||
nwGUI.mainMenu.aUpdates.activate(QAction.Trigger)
|
||||
|
||||
# qtbot.stop()
|
||||
nwUpdate._doClose()
|
||||
nwUpdate.close()
|
||||
|
||||
# END Test testDlgOther_Updates
|
||||
|
||||
@@ -101,13 +100,13 @@ def testDlgOther_EditLabel(qtbot, monkeypatch):
|
||||
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(GuiEditLabel, "result", lambda *a: QDialog.Accepted)
|
||||
newLabel, dlgOk = GuiEditLabel.getLabel(None, text="Hello World")
|
||||
newLabel, dlgOk = GuiEditLabel.getLabel(None, text="Hello World") # type: ignore
|
||||
assert dlgOk is True
|
||||
assert newLabel == "Hello World"
|
||||
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(GuiEditLabel, "result", lambda *a: QDialog.Rejected)
|
||||
newLabel, dlgOk = GuiEditLabel.getLabel(None, text="Hello World")
|
||||
newLabel, dlgOk = GuiEditLabel.getLabel(None, text="Hello World") # type: ignore
|
||||
assert dlgOk is False
|
||||
assert newLabel == "Hello World"
|
||||
|
||||
|
||||
@@ -45,23 +45,12 @@ def testDlgPreferences_Main(qtbot, monkeypatch, nwGUI, tstPaths):
|
||||
monkeypatch.setattr(GuiPreferences, "result", lambda *a: QDialog.Accepted)
|
||||
monkeypatch.setattr(SHARED._spelling, "listDictionaries", lambda: [("en", "English [en]")])
|
||||
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(GuiPreferences, "updateTheme", lambda *a: True)
|
||||
mp.setattr(GuiPreferences, "updateSyntax", lambda *a: True)
|
||||
mp.setattr(GuiPreferences, "needsRestart", lambda *a: True)
|
||||
mp.setattr(GuiPreferences, "refreshTree", lambda *a: True)
|
||||
nwGUI.mainMenu.aPreferences.activate(QAction.Trigger)
|
||||
qtbot.waitUntil(lambda: getGuiItem("GuiPreferences") is not None, timeout=1000)
|
||||
|
||||
nwGUI.mainMenu.aPreferences.activate(QAction.Trigger)
|
||||
qtbot.waitUntil(lambda: getGuiItem("GuiPreferences") is not None, timeout=1000)
|
||||
nwPrefs = getGuiItem("GuiPreferences")
|
||||
assert isinstance(nwPrefs, GuiPreferences)
|
||||
nwPrefs.show()
|
||||
|
||||
assert nwPrefs.updateTheme is False
|
||||
assert nwPrefs.updateSyntax is False
|
||||
assert nwPrefs.needsRestart is False
|
||||
assert nwPrefs.refreshTree is False
|
||||
|
||||
# General Settings
|
||||
qtbot.wait(KEY_DELAY)
|
||||
tabGeneral = nwPrefs.tabGeneral
|
||||
@@ -100,8 +89,6 @@ def testDlgPreferences_Main(qtbot, monkeypatch, nwGUI, tstPaths):
|
||||
qtbot.mouseClick(tabProjects.backupOnClose, Qt.LeftButton)
|
||||
assert tabProjects.backupOnClose.isChecked()
|
||||
|
||||
# qtbot.stop()
|
||||
|
||||
# Check Browse button
|
||||
monkeypatch.setattr(QFileDialog, "getExistingDirectory", lambda *a, **k: "")
|
||||
assert not tabProjects._backupFolder()
|
||||
@@ -204,7 +191,6 @@ def testDlgPreferences_Main(qtbot, monkeypatch, nwGUI, tstPaths):
|
||||
|
||||
# Save and Check Config
|
||||
qtbot.mouseClick(nwPrefs.buttonBox.button(QDialogButtonBox.Ok), Qt.LeftButton)
|
||||
nwPrefs._doClose()
|
||||
|
||||
assert CONFIG.saveConfig()
|
||||
projFile = tstPaths.cnfDir / "novelwriter.conf"
|
||||
|
||||
@@ -66,12 +66,12 @@ def testDlgProjDetails_Dialog(qtbot, nwGUI, prjLipsum):
|
||||
tocTab = projDet.tabContents
|
||||
tocTree = tocTab.tocTree
|
||||
assert tocTree.topLevelItemCount() == 7
|
||||
assert tocTree.topLevelItem(0).text(tocTab.C_TITLE) == "Lorem Ipsum"
|
||||
assert tocTree.topLevelItem(2).text(tocTab.C_TITLE) == "Prologue"
|
||||
assert tocTree.topLevelItem(3).text(tocTab.C_TITLE) == "Act One"
|
||||
assert tocTree.topLevelItem(4).text(tocTab.C_TITLE) == "Chapter One"
|
||||
assert tocTree.topLevelItem(5).text(tocTab.C_TITLE) == "Chapter Two"
|
||||
assert tocTree.topLevelItem(6).text(tocTab.C_TITLE) == "END"
|
||||
assert tocTree.topLevelItem(0).text(tocTab.C_TITLE) == "Lorem Ipsum" # type: ignore
|
||||
assert tocTree.topLevelItem(2).text(tocTab.C_TITLE) == "Prologue" # type: ignore
|
||||
assert tocTree.topLevelItem(3).text(tocTab.C_TITLE) == "Act One" # type: ignore
|
||||
assert tocTree.topLevelItem(4).text(tocTab.C_TITLE) == "Chapter One" # type: ignore
|
||||
assert tocTree.topLevelItem(5).text(tocTab.C_TITLE) == "Chapter Two" # type: ignore
|
||||
assert tocTree.topLevelItem(6).text(tocTab.C_TITLE) == "END" # type: ignore
|
||||
|
||||
# Count Pages
|
||||
tocTab.wpValue.setValue(100)
|
||||
@@ -82,8 +82,8 @@ def testDlgProjDetails_Dialog(qtbot, nwGUI, prjLipsum):
|
||||
thePages = ["1", "2", "1", "1", "11", "17", "0"]
|
||||
thePage = ["i", "ii", "1", "2", "3", "14", "31"]
|
||||
for i in range(7):
|
||||
assert tocTree.topLevelItem(i).text(tocTab.C_PAGES) == thePages[i]
|
||||
assert tocTree.topLevelItem(i).text(tocTab.C_PAGE) == thePage[i]
|
||||
assert tocTree.topLevelItem(i).text(tocTab.C_PAGES) == thePages[i] # type: ignore
|
||||
assert tocTree.topLevelItem(i).text(tocTab.C_PAGE) == thePage[i] # type: ignore
|
||||
|
||||
tocTab.poValue.setValue(5)
|
||||
tocTab.dblValue.setChecked(True)
|
||||
@@ -92,8 +92,8 @@ def testDlgProjDetails_Dialog(qtbot, nwGUI, prjLipsum):
|
||||
thePages = ["2", "2", "2", "2", "12", "18", "0"]
|
||||
thePage = ["i", "iii", "1", "3", "5", "17", "35"]
|
||||
for i in range(7):
|
||||
assert tocTree.topLevelItem(i).text(tocTab.C_PAGES) == thePages[i]
|
||||
assert tocTree.topLevelItem(i).text(tocTab.C_PAGE) == thePage[i]
|
||||
assert tocTree.topLevelItem(i).text(tocTab.C_PAGES) == thePages[i] # type: ignore
|
||||
assert tocTree.topLevelItem(i).text(tocTab.C_PAGE) == thePage[i] # type: ignore
|
||||
|
||||
# Re-populate
|
||||
assert tocTab._currentRoot is None
|
||||
@@ -103,7 +103,7 @@ def testDlgProjDetails_Dialog(qtbot, nwGUI, prjLipsum):
|
||||
# qtbot.stop()
|
||||
|
||||
# Clean Up
|
||||
projDet._doClose()
|
||||
projDet.close()
|
||||
nwGUI.closeMain()
|
||||
|
||||
# END Test testDlgProjDetails_Dialog
|
||||
|
||||
@@ -57,26 +57,26 @@ def testDlgProjSettings_Dialog(qtbot, monkeypatch, nwGUI):
|
||||
nwGUI.mainMenu.aProjectSettings.activate(QAction.Trigger)
|
||||
qtbot.waitUntil(lambda: getGuiItem("GuiProjectSettings") is not None, timeout=1000)
|
||||
|
||||
projEdit = getGuiItem("GuiProjectSettings")
|
||||
assert isinstance(projEdit, GuiProjectSettings)
|
||||
projEdit.show()
|
||||
qtbot.addWidget(projEdit)
|
||||
projSettings = getGuiItem("GuiProjectSettings")
|
||||
assert isinstance(projSettings, GuiProjectSettings)
|
||||
projSettings.show()
|
||||
qtbot.addWidget(projSettings)
|
||||
|
||||
# Switch Tabs
|
||||
projEdit._focusTab(GuiProjectSettings.TAB_REPLACE)
|
||||
assert projEdit._tabBox.currentWidget() == projEdit.tabReplace
|
||||
projSettings._focusTab(GuiProjectSettings.TAB_REPLACE)
|
||||
assert projSettings._tabBox.currentWidget() == projSettings.tabReplace
|
||||
|
||||
projEdit._focusTab(GuiProjectSettings.TAB_IMPORT)
|
||||
assert projEdit._tabBox.currentWidget() == projEdit.tabImport
|
||||
projSettings._focusTab(GuiProjectSettings.TAB_IMPORT)
|
||||
assert projSettings._tabBox.currentWidget() == projSettings.tabImport
|
||||
|
||||
projEdit._focusTab(GuiProjectSettings.TAB_STATUS)
|
||||
assert projEdit._tabBox.currentWidget() == projEdit.tabStatus
|
||||
projSettings._focusTab(GuiProjectSettings.TAB_STATUS)
|
||||
assert projSettings._tabBox.currentWidget() == projSettings.tabStatus
|
||||
|
||||
projEdit._focusTab(GuiProjectSettings.TAB_MAIN)
|
||||
assert projEdit._tabBox.currentWidget() == projEdit.tabMain
|
||||
projSettings._focusTab(GuiProjectSettings.TAB_MAIN)
|
||||
assert projSettings._tabBox.currentWidget() == projSettings.tabMain
|
||||
|
||||
# Clean Up
|
||||
projEdit._doClose()
|
||||
projSettings.close()
|
||||
# qtbot.stop()
|
||||
|
||||
# END Test testDlgProjSettings_Dialog
|
||||
@@ -93,10 +93,10 @@ def testDlgProjSettings_Main(qtbot, monkeypatch, nwGUI, fncPath, projPath, mockR
|
||||
CONFIG.setBackupPath(fncPath)
|
||||
|
||||
# Set some values
|
||||
theProject = SHARED.project
|
||||
theProject.data.setSpellLang("en")
|
||||
theProject.data.setAuthor("Jane Smith")
|
||||
theProject.data.setAutoReplace({"A": "B", "C": "D"})
|
||||
project = SHARED.project
|
||||
project.data.setSpellLang("en")
|
||||
project.data.setAuthor("Jane Smith")
|
||||
project.data.setAutoReplace({"A": "B", "C": "D"})
|
||||
|
||||
# Create Dialog
|
||||
projSettings = GuiProjectSettings(nwGUI, GuiProjectSettings.TAB_MAIN)
|
||||
@@ -130,12 +130,13 @@ def testDlgProjSettings_Main(qtbot, monkeypatch, nwGUI, fncPath, projPath, mockR
|
||||
assert tabMain.editAuthor.text() == "Jane Doe"
|
||||
|
||||
projSettings._doSave()
|
||||
assert theProject.data.name == "Project Name"
|
||||
assert theProject.data.title == "Project Title"
|
||||
assert theProject.data.author == "Jane Doe"
|
||||
assert project.data.name == "Project Name"
|
||||
assert project.data.title == "Project Title"
|
||||
assert project.data.author == "Jane Doe"
|
||||
|
||||
nwGUI._processProjectSettingsChanges()
|
||||
assert nwGUI.windowTitle() == "novelWriter - Project Name"
|
||||
|
||||
# Clean up
|
||||
projSettings._doClose()
|
||||
# qtbot.stop()
|
||||
|
||||
# END Test testDlgProjSettings_Main
|
||||
@@ -334,9 +335,7 @@ def testDlgProjSettings_StatusImport(qtbot, monkeypatch, nwGUI, fncPath, projPat
|
||||
assert importItems[C.iMain]["name"] == "Main"
|
||||
assert importItems["i000014"]["name"] == "Final"
|
||||
|
||||
# Clean up
|
||||
# qtbot.stop()
|
||||
projSettings._doClose()
|
||||
|
||||
# END Test testDlgProjSettings_StatusImport
|
||||
|
||||
@@ -422,8 +421,6 @@ def testDlgProjSettings_Replace(qtbot, monkeypatch, nwGUI, fncPath, projPath, mo
|
||||
"A": "B", "C": "D", "This": "With This Stuff"
|
||||
}
|
||||
|
||||
# Clean up
|
||||
# qtbot.stop()
|
||||
projSettings._doClose()
|
||||
|
||||
# END Test testDlgProjSettings_Replace
|
||||
|
||||
@@ -68,11 +68,11 @@ def testDlgWordList_Dialog(qtbot, monkeypatch, nwGUI, projPath):
|
||||
wList._loadWordList()
|
||||
|
||||
# Check that the content was loaded
|
||||
assert wList.listBox.item(0).text() == "word_a"
|
||||
assert wList.listBox.item(1).text() == "word_b"
|
||||
assert wList.listBox.item(2).text() == "word_c"
|
||||
assert wList.listBox.item(3).text() == "word_f"
|
||||
assert wList.listBox.item(4).text() == "word_g"
|
||||
assert wList.listBox.item(0).text() == "word_a" # type: ignore
|
||||
assert wList.listBox.item(1).text() == "word_b" # type: ignore
|
||||
assert wList.listBox.item(2).text() == "word_c" # type: ignore
|
||||
assert wList.listBox.item(3).text() == "word_f" # type: ignore
|
||||
assert wList.listBox.item(4).text() == "word_g" # type: ignore
|
||||
assert wList.listBox.count() == 5
|
||||
|
||||
# Add a blank word, which is ignored
|
||||
@@ -91,27 +91,27 @@ def testDlgWordList_Dialog(qtbot, monkeypatch, nwGUI, projPath):
|
||||
assert wList.listBox.count() == 6
|
||||
|
||||
# Check that the content now
|
||||
assert wList.listBox.item(0).text() == "word_a"
|
||||
assert wList.listBox.item(1).text() == "word_b"
|
||||
assert wList.listBox.item(2).text() == "word_c"
|
||||
assert wList.listBox.item(3).text() == "word_d"
|
||||
assert wList.listBox.item(4).text() == "word_f"
|
||||
assert wList.listBox.item(5).text() == "word_g"
|
||||
assert wList.listBox.item(0).text() == "word_a" # type: ignore
|
||||
assert wList.listBox.item(1).text() == "word_b" # type: ignore
|
||||
assert wList.listBox.item(2).text() == "word_c" # type: ignore
|
||||
assert wList.listBox.item(3).text() == "word_d" # type: ignore
|
||||
assert wList.listBox.item(4).text() == "word_f" # type: ignore
|
||||
assert wList.listBox.item(5).text() == "word_g" # type: ignore
|
||||
|
||||
# Delete a word
|
||||
wList.newEntry.setText("delete_me")
|
||||
wList._doAdd()
|
||||
assert wList.listBox.item(0).text() == "delete_me"
|
||||
assert wList.listBox.item(0).text() == "delete_me" # type: ignore
|
||||
|
||||
delItem = wList.listBox.findItems("delete_me", Qt.MatchExactly)[0]
|
||||
assert delItem.text() == "delete_me"
|
||||
delItem.setSelected(True)
|
||||
wList._doDelete()
|
||||
assert wList.listBox.findItems("delete_me", Qt.MatchExactly) == []
|
||||
assert wList.listBox.item(0).text() == "word_a"
|
||||
assert wList.listBox.item(0).text() == "word_a" # type: ignore
|
||||
|
||||
# Save files
|
||||
assert wList._doSave()
|
||||
wList._doSave()
|
||||
userDict.load()
|
||||
assert len(list(userDict)) == 6
|
||||
assert "word_a" in userDict
|
||||
@@ -122,6 +122,5 @@ def testDlgWordList_Dialog(qtbot, monkeypatch, nwGUI, projPath):
|
||||
assert "word_g" in userDict
|
||||
|
||||
# qtbot.stop()
|
||||
wList._doClose()
|
||||
|
||||
# END Test testDlgWordList_Dialog
|
||||
|
||||
@@ -29,6 +29,7 @@ from tools import (
|
||||
C, NWD_IGNORE, cmpFiles, buildTestProject, XML_IGNORE, getGuiItem, writeFile
|
||||
)
|
||||
|
||||
from PyQt5.QtGui import QColor, QPalette
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtWidgets import QDialog, QMenu, QMessageBox, QInputDialog
|
||||
|
||||
@@ -62,11 +63,6 @@ def testGuiMain_ProjectBlocker(nwGUI):
|
||||
assert nwGUI.openSelectedItem() is False
|
||||
assert nwGUI.editItemLabel() is False
|
||||
assert nwGUI.rebuildIndex() is False
|
||||
assert nwGUI.showProjectSettingsDialog() is False
|
||||
assert nwGUI.showProjectDetailsDialog() is False
|
||||
assert nwGUI.showBuildManuscriptDialog() is False
|
||||
assert nwGUI.showProjectWordListDialog() is False
|
||||
assert nwGUI.showWritingStatsDialog() is False
|
||||
|
||||
# END Test testGuiMain_ProjectBlocker
|
||||
|
||||
@@ -203,6 +199,28 @@ def testGuiMain_ProjectTreeItems(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
|
||||
# END Test testGuiMain_ProjectTreeItems
|
||||
|
||||
|
||||
@pytest.mark.gui
|
||||
def testGuiMain_UpdateTheme(qtbot, nwGUI):
|
||||
"""Test updating the theme in the GUI."""
|
||||
mainTheme = SHARED.theme
|
||||
CONFIG.guiTheme = "default_dark"
|
||||
CONFIG.guiSyntax = "default_dark"
|
||||
mainTheme.loadTheme()
|
||||
mainTheme.loadSyntax()
|
||||
nwGUI._processConfigChanges(True, True, True, True)
|
||||
|
||||
syntaxBack = QColor(*SHARED.theme.colBack)
|
||||
|
||||
assert nwGUI.docEditor.palette().color(QPalette.ColorRole.Window) == syntaxBack
|
||||
assert nwGUI.docEditor.docHeader.palette().color(QPalette.ColorRole.Window) == syntaxBack
|
||||
assert nwGUI.docViewer.palette().color(QPalette.ColorRole.Window) == syntaxBack
|
||||
assert nwGUI.docViewer.docHeader.palette().color(QPalette.ColorRole.Window) == syntaxBack
|
||||
|
||||
# qtbot.stop()
|
||||
|
||||
# END Test testGuiMain_UpdateTheme
|
||||
|
||||
|
||||
@pytest.mark.gui
|
||||
def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, projPath, tstPaths, mockRnd):
|
||||
"""Test the document editor."""
|
||||
|
||||
@@ -23,7 +23,6 @@ from __future__ import annotations
|
||||
import pytest
|
||||
|
||||
from pathlib import Path
|
||||
from configparser import ConfigParser
|
||||
|
||||
from mocked import causeOSError
|
||||
from tools import writeFile
|
||||
@@ -33,6 +32,7 @@ from PyQt5.QtWidgets import QApplication
|
||||
|
||||
from novelwriter import CONFIG, SHARED
|
||||
from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType
|
||||
from novelwriter.common import NWConfigParser
|
||||
from novelwriter.constants import nwLabels
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ def testGuiTheme_Main(qtbot, nwGUI, tstPaths):
|
||||
# Parse Colours
|
||||
# =============
|
||||
|
||||
parser = ConfigParser()
|
||||
parser = NWConfigParser()
|
||||
parser["Palette"] = {
|
||||
"colour1": "100, 150, 200",
|
||||
"colour2": "100, 150, 200, 250",
|
||||
|
||||
@@ -30,43 +30,39 @@ from novelwriter.tools.lipsum import GuiLipsum
|
||||
|
||||
|
||||
@pytest.mark.gui
|
||||
def testToolLipsum_Main(qtbot, nwGUI, projPath, mockRnd):
|
||||
"""Test the Lorem Ipsum tool.
|
||||
"""
|
||||
def testToolLipsum_Main(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
|
||||
"""Test the Lorem Ipsum tool."""
|
||||
# Check that we cannot open when there is no project
|
||||
nwGUI.mainMenu.aLipsumText.activate(QAction.Trigger)
|
||||
assert getGuiItem("GuiLipsum") is None
|
||||
|
||||
# Create a new project
|
||||
buildTestProject(nwGUI, projPath)
|
||||
assert nwGUI.openDocument(C.hSceneDoc) is True
|
||||
assert len(nwGUI.docEditor.getText()) == 15
|
||||
nwLipsum = GuiLipsum(nwGUI)
|
||||
|
||||
# Open the tool
|
||||
nwGUI.mainMenu.aLipsumText.activate(QAction.Trigger)
|
||||
qtbot.waitUntil(lambda: getGuiItem("GuiLipsum") is not None, timeout=1000)
|
||||
|
||||
nwLipsum = getGuiItem("GuiLipsum")
|
||||
assert isinstance(nwLipsum, GuiLipsum)
|
||||
|
||||
# Insert paragraphs
|
||||
# Generate paragraphs
|
||||
nwGUI.docEditor.setCursorPosition(100) # End of document
|
||||
nwLipsum.paraCount.setValue(2)
|
||||
nwLipsum._doInsert()
|
||||
theText = nwGUI.docEditor.getText()
|
||||
assert "Lorem ipsum" in theText
|
||||
assert len(theText) == 965
|
||||
assert "Lorem ipsum" in nwLipsum.lipsumText
|
||||
|
||||
# Insert random paragraph
|
||||
# Generate random paragraph
|
||||
nwGUI.docEditor.setCursorPosition(1000) # End of document
|
||||
nwLipsum.randSwitch.setChecked(True)
|
||||
nwLipsum.paraCount.setValue(1)
|
||||
nwLipsum._doInsert()
|
||||
theText = nwGUI.docEditor.getText()
|
||||
assert len(theText) > 965
|
||||
assert len(nwLipsum.lipsumText) > 0
|
||||
|
||||
# Close
|
||||
nwLipsum._doClose()
|
||||
nwLipsum.setObjectName("")
|
||||
nwLipsum.close()
|
||||
|
||||
# Trigger insertion in document
|
||||
assert nwGUI.openDocument(C.hSceneDoc) is True
|
||||
nwGUI.docEditor.setCursorLine(3)
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(GuiLipsum, "exec_", lambda *a: None)
|
||||
mp.setattr(GuiLipsum, "lipsumText", "FooBar")
|
||||
nwGUI.mainMenu.aLipsumText.activate(QAction.Trigger)
|
||||
assert nwGUI.docEditor.getText() == "### New Scene\n\nFooBar"
|
||||
|
||||
# qtbot.stop()
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ from tools import C, buildTestProject, getGuiItem
|
||||
from mocked import causeOSError
|
||||
|
||||
from PyQt5.QtCore import Qt, pyqtSlot
|
||||
from PyQt5.QtWidgets import QDialogButtonBox
|
||||
from PyQt5.QtWidgets import QAction, QDialogButtonBox
|
||||
from PyQt5.QtPrintSupport import QPrintPreviewDialog
|
||||
|
||||
from novelwriter import CONFIG, SHARED
|
||||
@@ -49,9 +49,11 @@ def testManuscript_Init(monkeypatch, qtbot: QtBot, nwGUI: GuiMain, projPath: Pat
|
||||
SHARED.project.storage.getDocument(C.hChapterDoc).writeDocument("## A Chapter\n\n\t\tHi")
|
||||
allText = "New Novel\nBy Jane Doe\nA Chapter\n\t\tHi\n* * *"
|
||||
|
||||
manus = GuiManuscript(nwGUI)
|
||||
nwGUI.mainMenu.aBuildManuscript.activate(QAction.Trigger)
|
||||
qtbot.waitUntil(lambda: getGuiItem("GuiManuscript") is not None, timeout=1000)
|
||||
manus = getGuiItem("GuiManuscript")
|
||||
assert isinstance(manus, GuiManuscript)
|
||||
manus.show()
|
||||
manus.loadContent()
|
||||
assert manus.docPreview.toPlainText().strip() == ""
|
||||
|
||||
# Run the default build
|
||||
@@ -79,7 +81,6 @@ def testManuscript_Init(monkeypatch, qtbot: QtBot, nwGUI: GuiMain, projPath: Pat
|
||||
assert manus.docPreview.toPlainText().strip() == ""
|
||||
manus.close()
|
||||
|
||||
# Finish
|
||||
# qtbot.stop()
|
||||
|
||||
# END Test testManuscript_Init
|
||||
|
||||
Reference in New Issue
Block a user