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
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)
@@ -68,7 +70,8 @@ class GuiPreferences(NPagedDialog):
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)
@@ -88,24 +91,16 @@ class GuiPreferences(NPagedDialog):
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
##
+3 -13
View File
@@ -71,8 +71,8 @@ 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")
@@ -95,21 +95,11 @@ class GuiProjectDetails(NPagedDialog):
def closeEvent(self, event: QCloseEvent) -> None:
"""Capture the close event and perform cleanup."""
self._saveGuiSettings()
event.accept()
self.deleteLater()
return
##
# Private Slots
##
@pyqtSlot()
def _doClose(self) -> None:
"""Save settings and close the dialog."""
self._saveGuiSettings()
self.close()
return
##
# Internal Functions
##
+17 -13
View File
@@ -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
##
+6 -2
View File
@@ -27,11 +27,11 @@ import logging
from typing import TYPE_CHECKING
from PyQt5.QtCore import Qt, pyqtSlot
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
@@ -45,6 +45,8 @@ logger = logging.getLogger(__name__)
class GuiWordList(QDialog):
newWordListReady = pyqtSignal()
def __init__(self, mainGui: GuiMain) -> None:
super().__init__(parent=mainGui)
@@ -166,6 +168,8 @@ class GuiWordList(QDialog):
if word:
userDict.add(word)
userDict.save()
self.newWordListReady.emit()
qApp.processEvents()
self.close()
return
+9 -9
View File
@@ -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()
@@ -594,7 +594,7 @@ 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.triggered.connect(self.mainGui.showLoremIpsumDialog)
return
@@ -872,7 +872,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 +902,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 +920,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 +961,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
+142 -174
View File
@@ -66,7 +66,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 +866,122 @@ 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:
@pyqtSlot()
def showLoremIpsumDialog(self) -> None:
"""Open the insert lorem ipsum text dialog."""
if not SHARED.hasProject:
logger.error("No project open")
return False
if SHARED.hasProject:
dialog = GuiLipsum(self)
dialog.setModal(False)
dialog.show()
dialog.raise_()
qApp.processEvents()
return
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 +1147,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."""