Update dialog handling of all dialogs

This commit is contained in:
Veronica Berglyd Olsen
2023-11-28 22:12:30 +01:00
parent 4e1fa01562
commit bf6cefa840
14 changed files with 265 additions and 311 deletions
+21 -34
View File
@@ -25,11 +25,11 @@ from __future__ import annotations
import logging import logging
from PyQt5.QtGui import QFont from PyQt5.QtGui import QCloseEvent, QFont
from PyQt5.QtCore import Qt, pyqtSlot from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
QDialog, QWidget, QComboBox, QSpinBox, QPushButton, QDialogButtonBox, QDialog, QWidget, QComboBox, QSpinBox, QPushButton, QDialogButtonBox,
QLineEdit, QFileDialog, QFontDialog, QDoubleSpinBox QLineEdit, QFileDialog, QFontDialog, QDoubleSpinBox, qApp
) )
from novelwriter import CONFIG, SHARED from novelwriter import CONFIG, SHARED
@@ -43,6 +43,8 @@ logger = logging.getLogger(__name__)
class GuiPreferences(NPagedDialog): class GuiPreferences(NPagedDialog):
newPreferencesReady = pyqtSignal(bool, bool, bool, bool)
def __init__(self, parent: QWidget) -> None: def __init__(self, parent: QWidget) -> None:
super().__init__(parent=parent) super().__init__(parent=parent)
@@ -68,7 +70,8 @@ class GuiPreferences(NPagedDialog):
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel, self) self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel, self)
self.buttonBox.accepted.connect(self._doSave) 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.addControls(self.buttonBox)
self.resize(*CONFIG.preferencesWinSize) self.resize(*CONFIG.preferencesWinSize)
@@ -88,24 +91,16 @@ class GuiPreferences(NPagedDialog):
return return
## ##
# Properties # Events
## ##
@property def closeEvent(self, event: QCloseEvent) -> None:
def updateTheme(self) -> bool: """Capture the close event and perform cleanup."""
return self._updateTheme logger.debug("Close: GuiPreferences")
self._saveWindowSize()
@property event.accept()
def updateSyntax(self) -> bool: self.deleteLater()
return self._updateSyntax return
@property
def needsRestart(self) -> bool:
return self._needsRestart
@property
def refreshTree(self) -> bool:
return self._refreshTree
## ##
# Private Slots # Private Slots
@@ -113,11 +108,7 @@ class GuiPreferences(NPagedDialog):
@pyqtSlot() @pyqtSlot()
def _doSave(self) -> None: def _doSave(self) -> None:
"""Trigger all the save functions in the tabs, and collect the """Trigger save functions in the tabs and emit ready signal."""
status of the saves.
"""
logger.debug("Saving new preferences")
self.tabGeneral.saveValues() self.tabGeneral.saveValues()
self.tabProjects.saveValues() self.tabProjects.saveValues()
self.tabDocs.saveValues() self.tabDocs.saveValues()
@@ -126,19 +117,15 @@ class GuiPreferences(NPagedDialog):
self.tabAuto.saveValues() self.tabAuto.saveValues()
self.tabQuote.saveValues() self.tabQuote.saveValues()
self._saveWindowSize()
CONFIG.saveConfig() CONFIG.saveConfig()
self.accept() self.newPreferencesReady.emit(
self._needsRestart, self._refreshTree, self._updateTheme, self._updateSyntax
)
qApp.processEvents()
self.close()
return return
@pyqtSlot()
def _doClose(self) -> None:
"""Close the preferences without saving the changes."""
self._saveWindowSize()
self.reject()
return
## ##
# Internal Functions # Internal Functions
## ##
+3 -13
View File
@@ -71,8 +71,8 @@ class GuiProjectDetails(NPagedDialog):
self.addTab(self.tabContents, self.tr("Contents")) self.addTab(self.tabContents, self.tr("Contents"))
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Close) self.buttonBox = QDialogButtonBox(QDialogButtonBox.Close)
self.buttonBox.button(QDialogButtonBox.Close) self.buttonBox.rejected.connect(self.close)
self.buttonBox.rejected.connect(self._doClose) self.rejected.connect(self.close)
self.addControls(self.buttonBox) self.addControls(self.buttonBox)
logger.debug("Ready: GuiProjectDetails") logger.debug("Ready: GuiProjectDetails")
@@ -95,21 +95,11 @@ class GuiProjectDetails(NPagedDialog):
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."""
self._saveGuiSettings()
event.accept() event.accept()
self.deleteLater() self.deleteLater()
return return
##
# Private Slots
##
@pyqtSlot()
def _doClose(self) -> None:
"""Save settings and close the dialog."""
self._saveGuiSettings()
self.close()
return
## ##
# Internal Functions # Internal Functions
## ##
+17 -13
View File
@@ -27,11 +27,11 @@ import logging
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from PyQt5.QtGui import QIcon, QPixmap, QColor from PyQt5.QtGui import QCloseEvent, QIcon, QPixmap, QColor
from PyQt5.QtCore import Qt, pyqtSlot from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
QColorDialog, QComboBox, QDialogButtonBox, QHBoxLayout, QLabel, QLineEdit, QColorDialog, QComboBox, QDialogButtonBox, QHBoxLayout, QLabel, QLineEdit,
QPushButton, QTreeWidget, QTreeWidgetItem, QVBoxLayout, QWidget QPushButton, QTreeWidget, QTreeWidgetItem, QVBoxLayout, QWidget, qApp
) )
from novelwriter import CONFIG, SHARED from novelwriter import CONFIG, SHARED
@@ -53,6 +53,8 @@ class GuiProjectSettings(NPagedDialog):
TAB_IMPORT = 2 TAB_IMPORT = 2
TAB_REPLACE = 3 TAB_REPLACE = 3
newProjectSettingsReady = pyqtSignal()
def __init__(self, mainGui: GuiMain, focusTab: int = TAB_MAIN) -> None: def __init__(self, mainGui: GuiMain, focusTab: int = TAB_MAIN) -> None:
super().__init__(parent=mainGui) super().__init__(parent=mainGui)
@@ -86,7 +88,8 @@ class GuiProjectSettings(NPagedDialog):
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
self.buttonBox.accepted.connect(self._doSave) 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.addControls(self.buttonBox)
# Focus Tab # Focus Tab
@@ -100,6 +103,13 @@ class GuiProjectSettings(NPagedDialog):
logger.debug("Delete: GuiProjectSettings") logger.debug("Delete: GuiProjectSettings")
return return
def closeEvent(self, event: QCloseEvent) -> None:
"""Capture the close event and perform cleanup."""
self._saveGuiSettings()
event.accept()
self.deleteLater()
return
## ##
# Private Slots # Private Slots
## ##
@@ -137,18 +147,12 @@ class GuiProjectSettings(NPagedDialog):
newList = self.tabReplace.getNewList() newList = self.tabReplace.getNewList()
project.data.setAutoReplace(newList) project.data.setAutoReplace(newList)
self._saveGuiSettings() self.newProjectSettingsReady.emit()
self.accept() qApp.processEvents()
self.close()
return return
@pyqtSlot()
def _doClose(self) -> None:
"""Save settings and close the dialog."""
self._saveGuiSettings()
self.reject()
return
## ##
# Internal Functions # Internal Functions
## ##
+6 -2
View File
@@ -27,11 +27,11 @@ import logging
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from PyQt5.QtCore import Qt, pyqtSlot
from PyQt5.QtGui import QCloseEvent from PyQt5.QtGui import QCloseEvent
from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
QAbstractItemView, QDialog, QDialogButtonBox, QHBoxLayout, QLabel, QAbstractItemView, QDialog, QDialogButtonBox, QHBoxLayout, QLabel,
QLineEdit, QListWidget, QListWidgetItem, QPushButton, QVBoxLayout QLineEdit, QListWidget, QListWidgetItem, QPushButton, QVBoxLayout, qApp
) )
from novelwriter import CONFIG, SHARED from novelwriter import CONFIG, SHARED
@@ -45,6 +45,8 @@ logger = logging.getLogger(__name__)
class GuiWordList(QDialog): class GuiWordList(QDialog):
newWordListReady = pyqtSignal()
def __init__(self, mainGui: GuiMain) -> None: def __init__(self, mainGui: GuiMain) -> None:
super().__init__(parent=mainGui) super().__init__(parent=mainGui)
@@ -166,6 +168,8 @@ class GuiWordList(QDialog):
if word: if word:
userDict.add(word) userDict.add(word)
userDict.save() userDict.save()
self.newWordListReady.emit()
qApp.processEvents()
self.close() self.close()
return return
+9 -9
View File
@@ -154,12 +154,12 @@ class GuiMainMenu(QMenuBar):
# Project > Project Settings # Project > Project Settings
self.aProjectSettings = self.projMenu.addAction(self.tr("Project Settings")) self.aProjectSettings = self.projMenu.addAction(self.tr("Project Settings"))
self.aProjectSettings.setShortcut("Ctrl+Shift+,") self.aProjectSettings.setShortcut("Ctrl+Shift+,")
self.aProjectSettings.triggered.connect(lambda: self.mainGui.showProjectSettingsDialog()) self.aProjectSettings.triggered.connect(self.mainGui.showProjectSettingsDialog)
# Project > Project Details # Project > Project Details
self.aProjectDetails = self.projMenu.addAction(self.tr("Project Details")) self.aProjectDetails = self.projMenu.addAction(self.tr("Project Details"))
self.aProjectDetails.setShortcut("Shift+F6") self.aProjectDetails.setShortcut("Shift+F6")
self.aProjectDetails.triggered.connect(lambda: self.mainGui.showProjectDetailsDialog()) self.aProjectDetails.triggered.connect(self.mainGui.showProjectDetailsDialog)
# Project > Separator # Project > Separator
self.projMenu.addSeparator() self.projMenu.addSeparator()
@@ -594,7 +594,7 @@ class GuiMainMenu(QMenuBar):
# Insert > Placeholder Text # Insert > Placeholder Text
self.aLipsumText = self.mInsBreaks.addAction(self.tr("Placeholder Text")) self.aLipsumText = self.mInsBreaks.addAction(self.tr("Placeholder Text"))
self.aLipsumText.triggered.connect(lambda: self.mainGui.showLoremIpsumDialog()) self.aLipsumText.triggered.connect(self.mainGui.showLoremIpsumDialog)
return return
@@ -872,7 +872,7 @@ class GuiMainMenu(QMenuBar):
# Tools > Project Word List # Tools > Project Word List
self.aEditWordList = self.toolsMenu.addAction(self.tr("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 # Tools > Add Dictionaries
if CONFIG.osWindows or CONFIG.isDebug: if CONFIG.osWindows or CONFIG.isDebug:
@@ -902,13 +902,13 @@ class GuiMainMenu(QMenuBar):
# Tools > Writing Statistics # Tools > Writing Statistics
self.aWritingStats = self.toolsMenu.addAction(self.tr("Writing Statistics")) self.aWritingStats = self.toolsMenu.addAction(self.tr("Writing Statistics"))
self.aWritingStats.setShortcut("F6") self.aWritingStats.setShortcut("F6")
self.aWritingStats.triggered.connect(lambda: self.mainGui.showWritingStatsDialog()) self.aWritingStats.triggered.connect(self.mainGui.showWritingStatsDialog)
# Tools > Preferences # Tools > Preferences
self.aPreferences = self.toolsMenu.addAction(self.tr("Preferences")) self.aPreferences = self.toolsMenu.addAction(self.tr("Preferences"))
self.aPreferences.setShortcut("Ctrl+,") self.aPreferences.setShortcut("Ctrl+,")
self.aPreferences.setMenuRole(QAction.PreferencesRole) self.aPreferences.setMenuRole(QAction.PreferencesRole)
self.aPreferences.triggered.connect(lambda: self.mainGui.showPreferencesDialog()) self.aPreferences.triggered.connect(self.mainGui.showPreferencesDialog)
return return
@@ -920,12 +920,12 @@ class GuiMainMenu(QMenuBar):
# Help > About # Help > About
self.aAboutNW = self.helpMenu.addAction(self.tr("About novelWriter")) self.aAboutNW = self.helpMenu.addAction(self.tr("About novelWriter"))
self.aAboutNW.setMenuRole(QAction.AboutRole) self.aAboutNW.setMenuRole(QAction.AboutRole)
self.aAboutNW.triggered.connect(lambda: self.mainGui.showAboutNWDialog()) self.aAboutNW.triggered.connect(self.mainGui.showAboutNWDialog)
# Help > About Qt5 # Help > About Qt5
self.aAboutQt = self.helpMenu.addAction(self.tr("About Qt5")) self.aAboutQt = self.helpMenu.addAction(self.tr("About Qt5"))
self.aAboutQt.setMenuRole(QAction.AboutQtRole) self.aAboutQt.setMenuRole(QAction.AboutQtRole)
self.aAboutQt.triggered.connect(lambda: self.mainGui.showAboutQtDialog()) self.aAboutQt.triggered.connect(self.mainGui.showAboutQtDialog)
# Help > Separator # Help > Separator
self.helpMenu.addSeparator() self.helpMenu.addSeparator()
@@ -961,7 +961,7 @@ class GuiMainMenu(QMenuBar):
# Document > Check for Updates # Document > Check for Updates
self.aUpdates = self.helpMenu.addAction(self.tr("Check for New Release")) 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 return
+142 -174
View File
@@ -66,7 +66,7 @@ from novelwriter.core.coretools import ProjectBuilder
from novelwriter.enum import ( from novelwriter.enum import (
nwDocAction, nwDocInsert, nwDocMode, nwItemType, nwItemClass, nwWidget, nwView nwDocAction, nwDocInsert, nwDocMode, nwItemType, nwItemClass, nwWidget, nwView
) )
from novelwriter.common import getGuiItem, hexToInt from novelwriter.common import hexToInt
from novelwriter.constants import nwFiles from novelwriter.constants import nwFiles
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -866,213 +866,122 @@ class GuiMain(QMainWindow):
return None return None
@pyqtSlot()
def showPreferencesDialog(self) -> None: def showPreferencesDialog(self) -> None:
"""Open the preferences dialog.""" """Open the preferences dialog."""
dlgConf = GuiPreferences(self) dialog = GuiPreferences(self)
dlgConf.exec_() dialog.newPreferencesReady.connect(self._processConfigChanges)
dialog.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()
return return
@pyqtSlot()
@pyqtSlot(int) @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.""" """Open the project settings dialog."""
if not SHARED.hasProject: if SHARED.hasProject:
logger.error("No project open") dialog = GuiProjectSettings(self, focusTab=focusTab)
return False dialog.newProjectSettingsReady.connect(self._processProjectSettingsChanges)
dialog.exec_()
dlgProj = GuiProjectSettings(self, focusTab=focusTab) return
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
@pyqtSlot() @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.""" """Open the build manuscript dialog."""
if not SHARED.hasProject: if SHARED.hasProject:
logger.error("No project open") dialog = GuiManuscript(self)
return False dialog.setModal(False)
dialog.show()
dialog.raise_()
qApp.processEvents()
dialog.loadContent()
return
dlgBuild = getGuiItem("GuiManuscript") @pyqtSlot()
if dlgBuild is None: def showLoremIpsumDialog(self) -> 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.""" """Open the insert lorem ipsum text dialog."""
if not SHARED.hasProject: if SHARED.hasProject:
logger.error("No project open") dialog = GuiLipsum(self)
return False dialog.setModal(False)
dialog.show()
dialog.raise_()
qApp.processEvents()
return
dlgLipsum = getGuiItem("GuiLipsum") @pyqtSlot()
if dlgLipsum is None: def showProjectWordListDialog(self) -> None:
dlgLipsum = GuiLipsum(self)
assert isinstance(dlgLipsum, GuiLipsum)
dlgLipsum.setModal(False)
dlgLipsum.show()
dlgLipsum.raise_()
qApp.processEvents()
return True
def showProjectWordListDialog(self) -> bool:
"""Open the project word list dialog.""" """Open the project word list dialog."""
if not SHARED.hasProject: if SHARED.hasProject:
logger.error("No project open") dialog = GuiWordList(self)
return False dialog.newWordListReady.connect(self._processWordListChanges)
dialog.exec_()
return
dlgWords = GuiWordList(self) @pyqtSlot()
dlgWords.exec_() def showWritingStatsDialog(self) -> None:
if dlgWords.result() == QDialog.Accepted:
logger.debug("Reloading word list")
SHARED.updateSpellCheckLanguage(reload=True)
self.docEditor.spellCheckDocument()
return True
def showWritingStatsDialog(self) -> bool:
"""Open the session stats dialog.""" """Open the session stats dialog."""
if not SHARED.hasProject: if SHARED.hasProject:
logger.error("No project open") dialog = GuiWritingStats(self)
return False dialog.setModal(False)
dialog.show()
dialog.raise_()
qApp.processEvents()
dialog.populateGUI()
return
dlgStats = getGuiItem("GuiWritingStats") @pyqtSlot()
if dlgStats is None: def showAboutNWDialog(self, showNotes: bool = False) -> None:
dlgStats = GuiWritingStats(self) """Show the novelWriter about dialog."""
assert isinstance(dlgStats, GuiWritingStats) dialog = GuiAbout(self)
dialog.setModal(True)
dlgStats.setModal(False) dialog.show()
dlgStats.show() dialog.raise_()
dlgStats.raise_()
qApp.processEvents() qApp.processEvents()
dlgStats.populateGUI() dialog.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()
if showNotes: if showNotes:
dlgAbout.showReleaseNotes() dialog.showReleaseNotes()
return
return True
@pyqtSlot()
def showAboutQtDialog(self) -> None: def showAboutQtDialog(self) -> None:
"""Show the about dialog for Qt.""" """Show the Qt about dialog."""
msgBox = QMessageBox(self) msgBox = QMessageBox(self)
msgBox.aboutQt(self, "About Qt") msgBox.aboutQt(self, "About Qt")
return return
@pyqtSlot()
def showUpdatesDialog(self) -> None: def showUpdatesDialog(self) -> None:
"""Show the check for updates dialog.""" """Show the check for updates dialog."""
dlgUpdate = getGuiItem("GuiUpdates") dialog = GuiUpdates(self)
if dlgUpdate is None: dialog.setModal(True)
dlgUpdate = GuiUpdates(self) dialog.show()
assert isinstance(dlgUpdate, GuiUpdates) dialog.raise_()
dlgUpdate.setModal(True)
dlgUpdate.show()
dlgUpdate.raise_()
qApp.processEvents() qApp.processEvents()
dlgUpdate.checkLatest() dialog.checkLatest()
return return
@pyqtSlot() @pyqtSlot()
def showDictionariesDialog(self) -> None: def showDictionariesDialog(self) -> None:
"""Show the download dictionaries dialog.""" """Show the download dictionaries dialog."""
dlgDicts = GuiDictionaries(self) dialog = GuiDictionaries(self)
dlgDicts.setModal(True) dialog.setModal(True)
dlgDicts.show() dialog.show()
dlgDicts.raise_() dialog.raise_()
qApp.processEvents() qApp.processEvents()
if not dlgDicts.initDialog(): if not dialog.initDialog():
dlgDicts.close() dialog.close()
SHARED.error(self.tr("Could not initialise the dialog.")) SHARED.error(self.tr("Could not initialise the dialog."))
return return
def reportConfErr(self) -> bool: def reportConfErr(self) -> bool:
@@ -1238,6 +1147,65 @@ class GuiMain(QMainWindow):
# Private Slots # 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) @pyqtSlot(str, nwDocMode)
def _followTag(self, tag: str, mode: nwDocMode) -> None: def _followTag(self, tag: str, mode: nwDocMode) -> None:
"""Follow a tag after user interaction with a link.""" """Follow a tag after user interaction with a link."""
+1 -1
View File
@@ -35,7 +35,7 @@ from novelwriter.dialogs.about import GuiAbout
def testDlgAbout_NWDialog(qtbot, monkeypatch, nwGUI): def testDlgAbout_NWDialog(qtbot, monkeypatch, nwGUI):
"""Test the novelWriter about dialogs.""" """Test the novelWriter about dialogs."""
# NW About # NW About
assert nwGUI.showAboutNWDialog(showNotes=True) is True nwGUI.showAboutNWDialog(showNotes=True)
qtbot.waitUntil(lambda: getGuiItem("GuiAbout") is not None, timeout=1000) qtbot.waitUntil(lambda: getGuiItem("GuiAbout") is not None, timeout=1000)
msgAbout = getGuiItem("GuiAbout") msgAbout = getGuiItem("GuiAbout")
+2 -16
View File
@@ -45,23 +45,12 @@ def testDlgPreferences_Main(qtbot, monkeypatch, nwGUI, tstPaths):
monkeypatch.setattr(GuiPreferences, "result", lambda *a: QDialog.Accepted) monkeypatch.setattr(GuiPreferences, "result", lambda *a: QDialog.Accepted)
monkeypatch.setattr(SHARED._spelling, "listDictionaries", lambda: [("en", "English [en]")]) monkeypatch.setattr(SHARED._spelling, "listDictionaries", lambda: [("en", "English [en]")])
with monkeypatch.context() as mp: nwGUI.mainMenu.aPreferences.activate(QAction.Trigger)
mp.setattr(GuiPreferences, "updateTheme", lambda *a: True) qtbot.waitUntil(lambda: getGuiItem("GuiPreferences") is not None, timeout=1000)
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)
nwPrefs = getGuiItem("GuiPreferences") nwPrefs = getGuiItem("GuiPreferences")
assert isinstance(nwPrefs, GuiPreferences) assert isinstance(nwPrefs, GuiPreferences)
nwPrefs.show() nwPrefs.show()
assert nwPrefs.updateTheme is False
assert nwPrefs.updateSyntax is False
assert nwPrefs.needsRestart is False
assert nwPrefs.refreshTree is False
# General Settings # General Settings
qtbot.wait(KEY_DELAY) qtbot.wait(KEY_DELAY)
tabGeneral = nwPrefs.tabGeneral tabGeneral = nwPrefs.tabGeneral
@@ -100,8 +89,6 @@ def testDlgPreferences_Main(qtbot, monkeypatch, nwGUI, tstPaths):
qtbot.mouseClick(tabProjects.backupOnClose, Qt.LeftButton) qtbot.mouseClick(tabProjects.backupOnClose, Qt.LeftButton)
assert tabProjects.backupOnClose.isChecked() assert tabProjects.backupOnClose.isChecked()
# qtbot.stop()
# Check Browse button # Check Browse button
monkeypatch.setattr(QFileDialog, "getExistingDirectory", lambda *a, **k: "") monkeypatch.setattr(QFileDialog, "getExistingDirectory", lambda *a, **k: "")
assert not tabProjects._backupFolder() assert not tabProjects._backupFolder()
@@ -204,7 +191,6 @@ def testDlgPreferences_Main(qtbot, monkeypatch, nwGUI, tstPaths):
# Save and Check Config # Save and Check Config
qtbot.mouseClick(nwPrefs.buttonBox.button(QDialogButtonBox.Ok), Qt.LeftButton) qtbot.mouseClick(nwPrefs.buttonBox.button(QDialogButtonBox.Ok), Qt.LeftButton)
nwPrefs._doClose()
assert CONFIG.saveConfig() assert CONFIG.saveConfig()
projFile = tstPaths.cnfDir / "novelwriter.conf" projFile = tstPaths.cnfDir / "novelwriter.conf"
+11 -11
View File
@@ -66,12 +66,12 @@ def testDlgProjDetails_Dialog(qtbot, nwGUI, prjLipsum):
tocTab = projDet.tabContents tocTab = projDet.tabContents
tocTree = tocTab.tocTree tocTree = tocTab.tocTree
assert tocTree.topLevelItemCount() == 7 assert tocTree.topLevelItemCount() == 7
assert tocTree.topLevelItem(0).text(tocTab.C_TITLE) == "Lorem Ipsum" assert tocTree.topLevelItem(0).text(tocTab.C_TITLE) == "Lorem Ipsum" # type: ignore
assert tocTree.topLevelItem(2).text(tocTab.C_TITLE) == "Prologue" assert tocTree.topLevelItem(2).text(tocTab.C_TITLE) == "Prologue" # type: ignore
assert tocTree.topLevelItem(3).text(tocTab.C_TITLE) == "Act One" assert tocTree.topLevelItem(3).text(tocTab.C_TITLE) == "Act One" # type: ignore
assert tocTree.topLevelItem(4).text(tocTab.C_TITLE) == "Chapter One" assert tocTree.topLevelItem(4).text(tocTab.C_TITLE) == "Chapter One" # type: ignore
assert tocTree.topLevelItem(5).text(tocTab.C_TITLE) == "Chapter Two" assert tocTree.topLevelItem(5).text(tocTab.C_TITLE) == "Chapter Two" # type: ignore
assert tocTree.topLevelItem(6).text(tocTab.C_TITLE) == "END" assert tocTree.topLevelItem(6).text(tocTab.C_TITLE) == "END" # type: ignore
# Count Pages # Count Pages
tocTab.wpValue.setValue(100) tocTab.wpValue.setValue(100)
@@ -82,8 +82,8 @@ def testDlgProjDetails_Dialog(qtbot, nwGUI, prjLipsum):
thePages = ["1", "2", "1", "1", "11", "17", "0"] thePages = ["1", "2", "1", "1", "11", "17", "0"]
thePage = ["i", "ii", "1", "2", "3", "14", "31"] thePage = ["i", "ii", "1", "2", "3", "14", "31"]
for i in range(7): for i in range(7):
assert tocTree.topLevelItem(i).text(tocTab.C_PAGES) == thePages[i] assert tocTree.topLevelItem(i).text(tocTab.C_PAGES) == thePages[i] # type: ignore
assert tocTree.topLevelItem(i).text(tocTab.C_PAGE) == thePage[i] assert tocTree.topLevelItem(i).text(tocTab.C_PAGE) == thePage[i] # type: ignore
tocTab.poValue.setValue(5) tocTab.poValue.setValue(5)
tocTab.dblValue.setChecked(True) tocTab.dblValue.setChecked(True)
@@ -92,8 +92,8 @@ def testDlgProjDetails_Dialog(qtbot, nwGUI, prjLipsum):
thePages = ["2", "2", "2", "2", "12", "18", "0"] thePages = ["2", "2", "2", "2", "12", "18", "0"]
thePage = ["i", "iii", "1", "3", "5", "17", "35"] thePage = ["i", "iii", "1", "3", "5", "17", "35"]
for i in range(7): for i in range(7):
assert tocTree.topLevelItem(i).text(tocTab.C_PAGES) == thePages[i] assert tocTree.topLevelItem(i).text(tocTab.C_PAGES) == thePages[i] # type: ignore
assert tocTree.topLevelItem(i).text(tocTab.C_PAGE) == thePage[i] assert tocTree.topLevelItem(i).text(tocTab.C_PAGE) == thePage[i] # type: ignore
# Re-populate # Re-populate
assert tocTab._currentRoot is None assert tocTab._currentRoot is None
@@ -103,7 +103,7 @@ def testDlgProjDetails_Dialog(qtbot, nwGUI, prjLipsum):
# qtbot.stop() # qtbot.stop()
# Clean Up # Clean Up
projDet._doClose() projDet.close()
nwGUI.closeMain() nwGUI.closeMain()
# END Test testDlgProjDetails_Dialog # END Test testDlgProjDetails_Dialog
+23 -26
View File
@@ -57,26 +57,26 @@ def testDlgProjSettings_Dialog(qtbot, monkeypatch, nwGUI):
nwGUI.mainMenu.aProjectSettings.activate(QAction.Trigger) nwGUI.mainMenu.aProjectSettings.activate(QAction.Trigger)
qtbot.waitUntil(lambda: getGuiItem("GuiProjectSettings") is not None, timeout=1000) qtbot.waitUntil(lambda: getGuiItem("GuiProjectSettings") is not None, timeout=1000)
projEdit = getGuiItem("GuiProjectSettings") projSettings = getGuiItem("GuiProjectSettings")
assert isinstance(projEdit, GuiProjectSettings) assert isinstance(projSettings, GuiProjectSettings)
projEdit.show() projSettings.show()
qtbot.addWidget(projEdit) qtbot.addWidget(projSettings)
# Switch Tabs # Switch Tabs
projEdit._focusTab(GuiProjectSettings.TAB_REPLACE) projSettings._focusTab(GuiProjectSettings.TAB_REPLACE)
assert projEdit._tabBox.currentWidget() == projEdit.tabReplace assert projSettings._tabBox.currentWidget() == projSettings.tabReplace
projEdit._focusTab(GuiProjectSettings.TAB_IMPORT) projSettings._focusTab(GuiProjectSettings.TAB_IMPORT)
assert projEdit._tabBox.currentWidget() == projEdit.tabImport assert projSettings._tabBox.currentWidget() == projSettings.tabImport
projEdit._focusTab(GuiProjectSettings.TAB_STATUS) projSettings._focusTab(GuiProjectSettings.TAB_STATUS)
assert projEdit._tabBox.currentWidget() == projEdit.tabStatus assert projSettings._tabBox.currentWidget() == projSettings.tabStatus
projEdit._focusTab(GuiProjectSettings.TAB_MAIN) projSettings._focusTab(GuiProjectSettings.TAB_MAIN)
assert projEdit._tabBox.currentWidget() == projEdit.tabMain assert projSettings._tabBox.currentWidget() == projSettings.tabMain
# Clean Up # Clean Up
projEdit._doClose() projSettings.close()
# qtbot.stop() # qtbot.stop()
# END Test testDlgProjSettings_Dialog # END Test testDlgProjSettings_Dialog
@@ -93,10 +93,10 @@ def testDlgProjSettings_Main(qtbot, monkeypatch, nwGUI, fncPath, projPath, mockR
CONFIG.setBackupPath(fncPath) CONFIG.setBackupPath(fncPath)
# Set some values # Set some values
theProject = SHARED.project project = SHARED.project
theProject.data.setSpellLang("en") project.data.setSpellLang("en")
theProject.data.setAuthor("Jane Smith") project.data.setAuthor("Jane Smith")
theProject.data.setAutoReplace({"A": "B", "C": "D"}) project.data.setAutoReplace({"A": "B", "C": "D"})
# Create Dialog # Create Dialog
projSettings = GuiProjectSettings(nwGUI, GuiProjectSettings.TAB_MAIN) 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" assert tabMain.editAuthor.text() == "Jane Doe"
projSettings._doSave() projSettings._doSave()
assert theProject.data.name == "Project Name" assert project.data.name == "Project Name"
assert theProject.data.title == "Project Title" assert project.data.title == "Project Title"
assert theProject.data.author == "Jane Doe" assert project.data.author == "Jane Doe"
nwGUI._processProjectSettingsChanges()
assert nwGUI.windowTitle() == "novelWriter - Project Name"
# Clean up
projSettings._doClose()
# qtbot.stop() # qtbot.stop()
# END Test testDlgProjSettings_Main # END Test testDlgProjSettings_Main
@@ -334,9 +335,7 @@ def testDlgProjSettings_StatusImport(qtbot, monkeypatch, nwGUI, fncPath, projPat
assert importItems[C.iMain]["name"] == "Main" assert importItems[C.iMain]["name"] == "Main"
assert importItems["i000014"]["name"] == "Final" assert importItems["i000014"]["name"] == "Final"
# Clean up
# qtbot.stop() # qtbot.stop()
projSettings._doClose()
# END Test testDlgProjSettings_StatusImport # 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" "A": "B", "C": "D", "This": "With This Stuff"
} }
# Clean up
# qtbot.stop() # qtbot.stop()
projSettings._doClose()
# END Test testDlgProjSettings_Replace # END Test testDlgProjSettings_Replace
-1
View File
@@ -122,6 +122,5 @@ def testDlgWordList_Dialog(qtbot, monkeypatch, nwGUI, projPath):
assert "word_g" in userDict assert "word_g" in userDict
# qtbot.stop() # qtbot.stop()
wList.close()
# END Test testDlgWordList_Dialog # END Test testDlgWordList_Dialog
+23 -5
View File
@@ -29,6 +29,7 @@ from tools import (
C, NWD_IGNORE, cmpFiles, buildTestProject, XML_IGNORE, getGuiItem, writeFile C, NWD_IGNORE, cmpFiles, buildTestProject, XML_IGNORE, getGuiItem, writeFile
) )
from PyQt5.QtGui import QColor, QPalette
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QDialog, QMenu, QMessageBox, QInputDialog from PyQt5.QtWidgets import QDialog, QMenu, QMessageBox, QInputDialog
@@ -62,11 +63,6 @@ def testGuiMain_ProjectBlocker(nwGUI):
assert nwGUI.openSelectedItem() is False assert nwGUI.openSelectedItem() is False
assert nwGUI.editItemLabel() is False assert nwGUI.editItemLabel() is False
assert nwGUI.rebuildIndex() 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 # END Test testGuiMain_ProjectBlocker
@@ -203,6 +199,28 @@ def testGuiMain_ProjectTreeItems(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
# END Test testGuiMain_ProjectTreeItems # 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 @pytest.mark.gui
def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, projPath, tstPaths, mockRnd): def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, projPath, tstPaths, mockRnd):
"""Test the document editor.""" """Test the document editor."""
+2 -2
View File
@@ -23,7 +23,6 @@ from __future__ import annotations
import pytest import pytest
from pathlib import Path from pathlib import Path
from configparser import ConfigParser
from mocked import causeOSError from mocked import causeOSError
from tools import writeFile from tools import writeFile
@@ -33,6 +32,7 @@ from PyQt5.QtWidgets import QApplication
from novelwriter import CONFIG, SHARED from novelwriter import CONFIG, SHARED
from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType
from novelwriter.common import NWConfigParser
from novelwriter.constants import nwLabels from novelwriter.constants import nwLabels
@@ -87,7 +87,7 @@ def testGuiTheme_Main(qtbot, nwGUI, tstPaths):
# Parse Colours # Parse Colours
# ============= # =============
parser = ConfigParser() parser = NWConfigParser()
parser["Palette"] = { parser["Palette"] = {
"colour1": "100, 150, 200", "colour1": "100, 150, 200",
"colour2": "100, 150, 200, 250", "colour2": "100, 150, 200, 250",
+5 -4
View File
@@ -30,7 +30,7 @@ from tools import C, buildTestProject, getGuiItem
from mocked import causeOSError from mocked import causeOSError
from PyQt5.QtCore import Qt, pyqtSlot from PyQt5.QtCore import Qt, pyqtSlot
from PyQt5.QtWidgets import QDialogButtonBox from PyQt5.QtWidgets import QAction, QDialogButtonBox
from PyQt5.QtPrintSupport import QPrintPreviewDialog from PyQt5.QtPrintSupport import QPrintPreviewDialog
from novelwriter import CONFIG, SHARED 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") 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* * *" 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.show()
manus.loadContent()
assert manus.docPreview.toPlainText().strip() == "" assert manus.docPreview.toPlainText().strip() == ""
# Run the default build # Run the default build
@@ -79,7 +81,6 @@ def testManuscript_Init(monkeypatch, qtbot: QtBot, nwGUI: GuiMain, projPath: Pat
assert manus.docPreview.toPlainText().strip() == "" assert manus.docPreview.toPlainText().strip() == ""
manus.close() manus.close()
# Finish
# qtbot.stop() # qtbot.stop()
# END Test testManuscript_Init # END Test testManuscript_Init