Update theme system (#1212)

This commit is contained in:
Veronica Berglyd Olsen
2022-10-29 00:50:26 +02:00
committed by GitHub
43 changed files with 1228 additions and 1188 deletions
+3 -1
View File
@@ -1,2 +1,4 @@
[Main] [Main]
name = Default System Theme name = Default Theme
description = Qt standard colours
icontheme = typicons_light
+8 -6
View File
@@ -1,10 +1,12 @@
[Main] [Main]
name = Default Dark Theme name = Default Dark Theme
author = Veronica Berglyd Olsen description = The novelWriter standard dark theme
credit = Veronica Berglyd Olsen author = Veronica Berglyd Olsen
url = https://github.com/vkbo/novelWriter credit = Veronica Berglyd Olsen
license = CC BY-SA 4.0 url = https://github.com/vkbo/novelWriter
licenseurl = https://creativecommons.org/licenses/by-sa/4.0/ license = CC BY-SA 4.0
licenseurl = https://creativecommons.org/licenses/by-sa/4.0/
icontheme = typicons_dark
[Palette] [Palette]
window = 54, 54, 54 window = 54, 54, 54
@@ -1,5 +0,0 @@
/**
* Default Theme: Dark
* This theme doesn't use any custom styles, so the file is only here as
* an example. There doesn't have to be a styles.qss file in the folder.
*/
@@ -5,6 +5,7 @@ credit = Ethan Schoonover
url = https://ethanschoonover.com/solarized/ url = https://ethanschoonover.com/solarized/
license = MIT license = MIT
licenseurl = https://github.com/altercation/solarized/blob/master/LICENSE licenseurl = https://github.com/altercation/solarized/blob/master/LICENSE
icontheme = typicons_dark
[Palette] [Palette]
window = 0, 43, 54 window = 0, 43, 54
@@ -5,6 +5,7 @@ credit = Ethan Schoonover
url = https://ethanschoonover.com/solarized/ url = https://ethanschoonover.com/solarized/
license = MIT license = MIT
licenseurl = https://github.com/altercation/solarized/blob/master/LICENSE licenseurl = https://github.com/altercation/solarized/blob/master/LICENSE
icontheme = typicons_light
[Palette] [Palette]
window = 238, 232, 213 window = 238, 232, 213
-15
View File
@@ -73,7 +73,6 @@ class Config:
# General # General
self.guiTheme = "" # GUI theme self.guiTheme = "" # GUI theme
self.guiSyntax = "" # Syntax theme self.guiSyntax = "" # Syntax theme
self.guiIcons = "" # Icon theme
self.guiFont = "" # Defaults to system default font self.guiFont = "" # Defaults to system default font
self.guiFontSize = 11 # Is overridden if system default is loaded self.guiFontSize = 11 # Is overridden if system default is loaded
self.guiScale = 1.0 # Set automatically by Theme class self.guiScale = 1.0 # Set automatically by Theme class
@@ -81,7 +80,6 @@ class Config:
self.setDefaultGuiTheme() self.setDefaultGuiTheme()
self.setDefaultSyntaxTheme() self.setDefaultSyntaxTheme()
self.setDefaultIconTheme()
# Localisation # Localisation
self.qLocal = QLocale.system() self.qLocal = QLocale.system()
@@ -410,7 +408,6 @@ class Config:
cnfSec = "Main" cnfSec = "Main"
self.guiTheme = theConf.rdStr(cnfSec, "theme", self.guiTheme) self.guiTheme = theConf.rdStr(cnfSec, "theme", self.guiTheme)
self.guiSyntax = theConf.rdStr(cnfSec, "syntax", self.guiSyntax) self.guiSyntax = theConf.rdStr(cnfSec, "syntax", self.guiSyntax)
self.guiIcons = theConf.rdStr(cnfSec, "icons", self.guiIcons)
self.guiFont = theConf.rdStr(cnfSec, "guifont", self.guiFont) self.guiFont = theConf.rdStr(cnfSec, "guifont", self.guiFont)
self.guiFontSize = theConf.rdInt(cnfSec, "guifontsize", self.guiFontSize) self.guiFontSize = theConf.rdInt(cnfSec, "guifontsize", self.guiFontSize)
self.lastNotes = theConf.rdStr(cnfSec, "lastnotes", self.lastNotes) self.lastNotes = theConf.rdStr(cnfSec, "lastnotes", self.lastNotes)
@@ -508,12 +505,6 @@ class Config:
logger.info("Using straight double quotes, so disabling auto-replace") logger.info("Using straight double quotes, so disabling auto-replace")
self.doReplaceDQuote = False self.doReplaceDQuote = False
# Check deprecated settings
if self.guiIcons in ("typicons_colour_dark", "typicons_grey_dark"):
self.guiIcons = "typicons_dark"
elif self.guiIcons in ("typicons_colour_light", "typicons_grey_light"):
self.guiIcons = "typicons_light"
return True return True
def saveConfig(self): def saveConfig(self):
@@ -529,7 +520,6 @@ class Config:
"timestamp": formatTimeStamp(time()), "timestamp": formatTimeStamp(time()),
"theme": str(self.guiTheme), "theme": str(self.guiTheme),
"syntax": str(self.guiSyntax), "syntax": str(self.guiSyntax),
"icons": str(self.guiIcons),
"guifont": str(self.guiFont), "guifont": str(self.guiFont),
"guifontsize": str(self.guiFontSize), "guifontsize": str(self.guiFontSize),
"lastnotes": str(self.lastNotes), "lastnotes": str(self.lastNotes),
@@ -816,11 +806,6 @@ class Config:
""" """
self.guiSyntax = "default_light" self.guiSyntax = "default_light"
def setDefaultIconTheme(self):
"""Reset the icon theme to default value.
"""
self.guiIcons = "typicons_light"
## ##
# Getters # Getters
## ##
+36 -41
View File
@@ -34,7 +34,6 @@ from PyQt5.QtWidgets import (
QLineEdit, QFileDialog, QFontDialog, QDoubleSpinBox QLineEdit, QFileDialog, QFontDialog, QDoubleSpinBox
) )
from novelwriter.enum import nwAlert
from novelwriter.custom import QSwitch, QConfigLayout, PagedDialog from novelwriter.custom import QSwitch, QConfigLayout, PagedDialog
from novelwriter.dialogs.quotes import GuiQuoteSelect from novelwriter.dialogs.quotes import GuiQuoteSelect
@@ -78,10 +77,36 @@ class GuiPreferences(PagedDialog):
self.resize(*self.mainConf.getPreferencesSize()) self.resize(*self.mainConf.getPreferencesSize())
# Settings
self._updateTheme = False
self._updateSyntax = False
self._needsRestart = False
self._refreshTree = False
logger.debug("GuiPreferences initialisation complete") logger.debug("GuiPreferences initialisation complete")
return return
##
# Properties
##
@property
def updateTheme(self):
return self._updateTheme
@property
def updateSyntax(self):
return self._updateSyntax
@property
def needsRestart(self):
return self._needsRestart
@property
def refreshTree(self):
return self._refreshTree
## ##
# Slots # Slots
## ##
@@ -92,8 +117,7 @@ class GuiPreferences(PagedDialog):
""" """
logger.debug("Saving new preferences") logger.debug("Saving new preferences")
needsRestart, refreshTree = self.tabGeneral.saveValues() self.tabGeneral.saveValues()
self.tabProjects.saveValues() self.tabProjects.saveValues()
self.tabDocs.saveValues() self.tabDocs.saveValues()
self.tabEditor.saveValues() self.tabEditor.saveValues()
@@ -101,14 +125,6 @@ class GuiPreferences(PagedDialog):
self.tabAuto.saveValues() self.tabAuto.saveValues()
self.tabQuote.saveValues() self.tabQuote.saveValues()
if needsRestart:
self.mainGui.makeAlert(self.tr(
"Some changes will not be applied until novelWriter has been restarted."
), nwAlert.INFO)
if refreshTree:
self.mainGui.projView.populateTree()
self._saveWindowSize() self._saveWindowSize()
self.accept() self.accept()
@@ -140,6 +156,7 @@ class GuiPreferencesGeneral(QWidget):
super().__init__(parent=prefsGui) super().__init__(parent=prefsGui)
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
self.prefsGui = prefsGui
self.mainGui = prefsGui.mainGui self.mainGui = prefsGui.mainGui
self.mainTheme = prefsGui.mainGui.mainTheme self.mainTheme = prefsGui.mainGui.mainTheme
@@ -185,22 +202,6 @@ class GuiPreferencesGeneral(QWidget):
self.tr("Requires restart.") self.tr("Requires restart.")
) )
# Select Icon Theme
self.guiIcons = QComboBox()
self.guiIcons.setMinimumWidth(minWidth)
self.iconCache = self.mainTheme.iconCache.listThemes()
for iconDir, iconName in self.iconCache:
self.guiIcons.addItem(iconName, iconDir)
iconIdx = self.guiIcons.findData(self.mainConf.guiIcons)
if iconIdx != -1:
self.guiIcons.setCurrentIndex(iconIdx)
self.mainForm.addRow(
self.tr("Main icon theme"),
self.guiIcons,
self.tr("Requires restart.")
)
# Editor Theme # Editor Theme
self.guiSyntax = QComboBox() self.guiSyntax = QComboBox()
self.guiSyntax.setMinimumWidth(self.mainConf.pxInt(200)) self.guiSyntax.setMinimumWidth(self.mainConf.pxInt(200))
@@ -288,27 +289,21 @@ class GuiPreferencesGeneral(QWidget):
""" """
guiLang = self.guiLang.currentData() guiLang = self.guiLang.currentData()
guiTheme = self.guiTheme.currentData() guiTheme = self.guiTheme.currentData()
guiIcons = self.guiIcons.currentData()
guiSyntax = self.guiSyntax.currentData() guiSyntax = self.guiSyntax.currentData()
guiFont = self.guiFont.text() guiFont = self.guiFont.text()
guiFontSize = self.guiFontSize.value() guiFontSize = self.guiFontSize.value()
emphLabels = self.emphLabels.isChecked() emphLabels = self.emphLabels.isChecked()
# Check if restart is needed # Update Flags
needsRestart = False self.prefsGui._updateTheme |= self.mainConf.guiTheme != guiTheme
needsRestart |= self.mainConf.guiLang != guiLang self.prefsGui._updateSyntax |= self.mainConf.guiSyntax != guiSyntax
needsRestart |= self.mainConf.guiTheme != guiTheme self.prefsGui._needsRestart |= self.mainConf.guiLang != guiLang
needsRestart |= self.mainConf.guiIcons != guiIcons self.prefsGui._needsRestart |= self.mainConf.guiFont != guiFont
needsRestart |= self.mainConf.guiFont != guiFont self.prefsGui._needsRestart |= self.mainConf.guiFontSize != guiFontSize
needsRestart |= self.mainConf.guiFontSize != guiFontSize self.prefsGui._refreshTree |= self.mainConf.emphLabels != emphLabels
# Check if refreshing project tree is needed
refreshTree = False
refreshTree |= self.mainConf.emphLabels != emphLabels
self.mainConf.guiLang = guiLang self.mainConf.guiLang = guiLang
self.mainConf.guiTheme = guiTheme self.mainConf.guiTheme = guiTheme
self.mainConf.guiIcons = guiIcons
self.mainConf.guiSyntax = guiSyntax self.mainConf.guiSyntax = guiSyntax
self.mainConf.guiFont = guiFont self.mainConf.guiFont = guiFont
self.mainConf.guiFontSize = guiFontSize self.mainConf.guiFontSize = guiFontSize
@@ -319,7 +314,7 @@ class GuiPreferencesGeneral(QWidget):
self.mainConf.confChanged = True self.mainConf.confChanged = True
return needsRestart, refreshTree return
## ##
# Slots # Slots
+127 -69
View File
@@ -138,24 +138,20 @@ class GuiDocEditor(QTextEdit):
self.setFrameStyle(QFrame.NoFrame) self.setFrameStyle(QFrame.NoFrame)
# Custom Shortcuts # Custom Shortcuts
QShortcut( self.keyContext = QShortcut(self)
QKeySequence("Ctrl+."), self.keyContext.setKey("Ctrl+.")
self, self.keyContext.setContext(Qt.WidgetShortcut)
context=Qt.WidgetShortcut, self.keyContext.activated.connect(self._openSpellContext)
activated=self._openSpellContext
) self.followTag1 = QShortcut(self)
QShortcut( self.followTag1.setKey(Qt.Key_Return | Qt.ControlModifier)
Qt.Key_Return | Qt.ControlModifier, self.followTag1.setContext(Qt.WidgetShortcut)
self, self.followTag1.activated.connect(self._followTag)
context=Qt.WidgetShortcut,
activated=self._followTag self.followTag2 = QShortcut(self)
) self.followTag2.setKey(Qt.Key_Enter | Qt.ControlModifier)
QShortcut( self.followTag2.setContext(Qt.WidgetShortcut)
Qt.Key_Enter | Qt.ControlModifier, self.followTag2.activated.connect(self._followTag)
self,
context=Qt.WidgetShortcut,
activated=self._followTag
)
# Set Up Document Word Counter # Set Up Document Word Counter
self.wcTimerDoc = QTimer() self.wcTimerDoc = QTimer()
@@ -177,6 +173,7 @@ class GuiDocEditor(QTextEdit):
self.wCounterSel.signals.countsReady.connect(self._updateSelCounts) self.wCounterSel.signals.countsReady.connect(self._updateSelCounts)
# Finalise # Finalise
self.updateSyntaxColours()
self.initEditor() self.initEditor()
logger.debug("GuiDocEditor initialisation complete") logger.debug("GuiDocEditor initialisation complete")
@@ -210,6 +207,35 @@ class GuiDocEditor(QTextEdit):
return True return True
def updateTheme(self):
"""Update theme elements
"""
self.docSearch.updateTheme()
self.docHeader.updateTheme()
self.docFooter.updateTheme()
return
def updateSyntaxColours(self):
"""Update the syntax highlighting theme.
"""
mainPalette = self.palette()
mainPalette.setColor(QPalette.Window, QColor(*self.mainTheme.colBack))
mainPalette.setColor(QPalette.Base, QColor(*self.mainTheme.colBack))
mainPalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText))
self.setPalette(mainPalette)
docPalette = self.viewport().palette()
docPalette.setColor(QPalette.Base, QColor(*self.mainTheme.colBack))
docPalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText))
self.viewport().setPalette(docPalette)
self.docHeader.matchColours()
self.docFooter.matchColours()
self.highLight.initHighlighter()
return
def initEditor(self): def initEditor(self):
"""Initialise or re-initialise the editor with the user's """Initialise or re-initialise the editor with the user's
settings. This function is both called when the editor is settings. This function is both called when the editor is
@@ -256,21 +282,6 @@ class GuiDocEditor(QTextEdit):
theFont.setPointSize(self.mainConf.textSize) theFont.setPointSize(self.mainConf.textSize)
self.setFont(theFont) self.setFont(theFont)
# Set the widget colours to match syntax theme
mainPalette = self.palette()
mainPalette.setColor(QPalette.Window, QColor(*self.mainTheme.colBack))
mainPalette.setColor(QPalette.Base, QColor(*self.mainTheme.colBack))
mainPalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText))
self.setPalette(mainPalette)
docPalette = self.viewport().palette()
docPalette.setColor(QPalette.Base, QColor(*self.mainTheme.colBack))
docPalette.setColor(QPalette.Text, QColor(*self.mainTheme.colText))
self.viewport().setPalette(docPalette)
self.docHeader.matchColours()
self.docFooter.matchColours()
# Set default text margins # Set default text margins
# Due to cursor visibility, a part of the margin must be # Due to cursor visibility, a part of the margin must be
# allocated to the document itself. See issue #1112. # allocated to the document itself. See issue #1112.
@@ -305,9 +316,6 @@ class GuiDocEditor(QTextEdit):
# Refresh the tab stops # Refresh the tab stops
self.setTabStopDistance(self.mainConf.getTabWidth()) self.setTabStopDistance(self.mainConf.getTabWidth())
# Initialise the syntax highlighter
self.highLight.initHighlighter()
# Configure word count timer # Configure word count timer
self.wcInterval = self.mainConf.wordCountTimer self.wcInterval = self.mainConf.wordCountTimer
self.wcTimerDoc.setInterval(int(self.wcInterval*1000)) self.wcTimerDoc.setInterval(int(self.wcInterval*1000))
@@ -1162,6 +1170,7 @@ class GuiDocEditor(QTextEdit):
posCursor = self.cursorForPosition(thePos) posCursor = self.cursorForPosition(thePos)
spellCheck = self._spellCheck spellCheck = self._spellCheck
theWord = ""
if posCursor.block().text().startswith("@"): if posCursor.block().text().startswith("@"):
spellCheck = False spellCheck = False
@@ -1430,6 +1439,7 @@ class GuiDocEditor(QTextEdit):
origB = theCursor.selectionEnd() origB = theCursor.selectionEnd()
else: else:
origA = theCursor.position() origA = theCursor.position()
origB = theCursor.position()
findOpt = QTextDocument.FindFlag(0) findOpt = QTextDocument.FindFlag(0)
if self.docSearch.isCaseSense: if self.docSearch.isCaseSense:
@@ -2253,7 +2263,6 @@ class GuiDocEditSearch(QFrame):
self.searchOpt.setToolButtonStyle(Qt.ToolButtonIconOnly) self.searchOpt.setToolButtonStyle(Qt.ToolButtonIconOnly)
self.searchOpt.setIconSize(QSize(tPx, tPx)) self.searchOpt.setIconSize(QSize(tPx, tPx))
self.searchOpt.setContentsMargins(0, 0, 0, 0) self.searchOpt.setContentsMargins(0, 0, 0, 0)
self.searchOpt.setStyleSheet("QToolBar {padding: 0;}")
self.searchLabel = QLabel(self.tr("Search")) self.searchLabel = QLabel(self.tr("Search"))
self.searchLabel.setFont(self.boxFont) self.searchLabel.setFont(self.boxFont)
@@ -2264,35 +2273,30 @@ class GuiDocEditSearch(QFrame):
self.resultLabel.setMinimumWidth(self.mainTheme.getTextWidth("?/?", self.boxFont)) self.resultLabel.setMinimumWidth(self.mainTheme.getTextWidth("?/?", self.boxFont))
self.toggleCase = QAction(self.tr("Case Sensitive"), self) self.toggleCase = QAction(self.tr("Case Sensitive"), self)
self.toggleCase.setIcon(self.mainTheme.getIcon("search_case"))
self.toggleCase.setCheckable(True) self.toggleCase.setCheckable(True)
self.toggleCase.setChecked(self.isCaseSense) self.toggleCase.setChecked(self.isCaseSense)
self.toggleCase.toggled.connect(self._doToggleCase) self.toggleCase.toggled.connect(self._doToggleCase)
self.searchOpt.addAction(self.toggleCase) self.searchOpt.addAction(self.toggleCase)
self.toggleWord = QAction(self.tr("Whole Words Only"), self) self.toggleWord = QAction(self.tr("Whole Words Only"), self)
self.toggleWord.setIcon(self.mainTheme.getIcon("search_word"))
self.toggleWord.setCheckable(True) self.toggleWord.setCheckable(True)
self.toggleWord.setChecked(self.isWholeWord) self.toggleWord.setChecked(self.isWholeWord)
self.toggleWord.toggled.connect(self._doToggleWord) self.toggleWord.toggled.connect(self._doToggleWord)
self.searchOpt.addAction(self.toggleWord) self.searchOpt.addAction(self.toggleWord)
self.toggleRegEx = QAction(self.tr("RegEx Mode"), self) self.toggleRegEx = QAction(self.tr("RegEx Mode"), self)
self.toggleRegEx.setIcon(self.mainTheme.getIcon("search_regex"))
self.toggleRegEx.setCheckable(True) self.toggleRegEx.setCheckable(True)
self.toggleRegEx.setChecked(self.isRegEx) self.toggleRegEx.setChecked(self.isRegEx)
self.toggleRegEx.toggled.connect(self._doToggleRegEx) self.toggleRegEx.toggled.connect(self._doToggleRegEx)
self.searchOpt.addAction(self.toggleRegEx) self.searchOpt.addAction(self.toggleRegEx)
self.toggleLoop = QAction(self.tr("Loop Search"), self) self.toggleLoop = QAction(self.tr("Loop Search"), self)
self.toggleLoop.setIcon(self.mainTheme.getIcon("search_loop"))
self.toggleLoop.setCheckable(True) self.toggleLoop.setCheckable(True)
self.toggleLoop.setChecked(self.doLoop) self.toggleLoop.setChecked(self.doLoop)
self.toggleLoop.toggled.connect(self._doToggleLoop) self.toggleLoop.toggled.connect(self._doToggleLoop)
self.searchOpt.addAction(self.toggleLoop) self.searchOpt.addAction(self.toggleLoop)
self.toggleProject = QAction(self.tr("Search Next File"), self) self.toggleProject = QAction(self.tr("Search Next File"), self)
self.toggleProject.setIcon(self.mainTheme.getIcon("search_project"))
self.toggleProject.setCheckable(True) self.toggleProject.setCheckable(True)
self.toggleProject.setChecked(self.doNextFile) self.toggleProject.setChecked(self.doNextFile)
self.toggleProject.toggled.connect(self._doToggleProject) self.toggleProject.toggled.connect(self._doToggleProject)
@@ -2301,7 +2305,6 @@ class GuiDocEditSearch(QFrame):
self.searchOpt.addSeparator() self.searchOpt.addSeparator()
self.toggleMatchCap = QAction(self.tr("Preserve Case"), self) self.toggleMatchCap = QAction(self.tr("Preserve Case"), self)
self.toggleMatchCap.setIcon(self.mainTheme.getIcon("search_preserve"))
self.toggleMatchCap.setCheckable(True) self.toggleMatchCap.setCheckable(True)
self.toggleMatchCap.setChecked(self.doMatchCap) self.toggleMatchCap.setChecked(self.doMatchCap)
self.toggleMatchCap.toggled.connect(self._doToggleMatchCap) self.toggleMatchCap.toggled.connect(self._doToggleMatchCap)
@@ -2310,7 +2313,6 @@ class GuiDocEditSearch(QFrame):
self.searchOpt.addSeparator() self.searchOpt.addSeparator()
self.cancelSearch = QAction(self.tr("Close Search"), self) self.cancelSearch = QAction(self.tr("Close Search"), self)
self.cancelSearch.setIcon(self.mainTheme.getIcon("search_cancel"))
self.cancelSearch.triggered.connect(self._doClose) self.cancelSearch.triggered.connect(self._doClose)
self.searchOpt.addAction(self.cancelSearch) self.searchOpt.addAction(self.cancelSearch)
@@ -2322,15 +2324,14 @@ class GuiDocEditSearch(QFrame):
self.showReplace = QToolButton(self) self.showReplace = QToolButton(self)
self.showReplace.setArrowType(Qt.RightArrow) self.showReplace.setArrowType(Qt.RightArrow)
self.showReplace.setCheckable(True) self.showReplace.setCheckable(True)
self.showReplace.setStyleSheet("QToolButton {border: none; background: transparent;}")
self.showReplace.toggled.connect(self._doToggleReplace) self.showReplace.toggled.connect(self._doToggleReplace)
self.searchButton = QPushButton(self.mainTheme.getIcon("search"), "") self.searchButton = QPushButton("")
self.searchButton.setFixedSize(QSize(bPx, bPx)) self.searchButton.setFixedSize(QSize(bPx, bPx))
self.searchButton.setToolTip(self.tr("Find in current document")) self.searchButton.setToolTip(self.tr("Find in current document"))
self.searchButton.clicked.connect(self._doSearch) self.searchButton.clicked.connect(self._doSearch)
self.replaceButton = QPushButton(self.mainTheme.getIcon("search_replace"), "") self.replaceButton = QPushButton("")
self.replaceButton.setFixedSize(QSize(bPx, bPx)) self.replaceButton.setFixedSize(QSize(bPx, bPx))
self.replaceButton.setToolTip(self.tr("Find and replace in current document")) self.replaceButton.setToolTip(self.tr("Find and replace in current document"))
self.replaceButton.clicked.connect(self._doReplace) self.replaceButton.clicked.connect(self._doReplace)
@@ -2360,6 +2361,35 @@ class GuiDocEditSearch(QFrame):
self.replaceButton.setVisible(False) self.replaceButton.setVisible(False)
self.adjustSize() self.adjustSize()
self.updateTheme()
logger.debug("GuiDocEditSearch initialisation complete")
return
def updateTheme(self):
"""Update theme elements.
"""
qPalette = qApp.palette()
self.setPalette(qPalette)
self.searchBox.setPalette(qPalette)
self.replaceBox.setPalette(qPalette)
# Set icons
self.toggleCase.setIcon(self.mainTheme.getIcon("search_case"))
self.toggleWord.setIcon(self.mainTheme.getIcon("search_word"))
self.toggleRegEx.setIcon(self.mainTheme.getIcon("search_regex"))
self.toggleLoop.setIcon(self.mainTheme.getIcon("search_loop"))
self.toggleProject.setIcon(self.mainTheme.getIcon("search_project"))
self.toggleMatchCap.setIcon(self.mainTheme.getIcon("search_preserve"))
self.cancelSearch.setIcon(self.mainTheme.getIcon("search_cancel"))
self.searchButton.setIcon(self.mainTheme.getIcon("search"))
self.replaceButton.setIcon(self.mainTheme.getIcon("search_replace"))
# Set stylesheets
self.searchOpt.setStyleSheet("QToolBar {padding: 0;}")
self.showReplace.setStyleSheet("QToolButton {border: none; background: transparent;}")
# Construct Box Colours # Construct Box Colours
qPalette = self.searchBox.palette() qPalette = self.searchBox.palette()
baseCol = qPalette.base().color() baseCol = qPalette.base().color()
@@ -2378,8 +2408,6 @@ class GuiDocEditSearch(QFrame):
False: errCol False: errCol
} }
logger.debug("GuiDocEditSearch initialisation complete")
return return
def closeSearch(self): def closeSearch(self):
@@ -2405,10 +2433,10 @@ class GuiDocEditSearch(QFrame):
""" """
if self.replaceBox.isVisible(): if self.replaceBox.isVisible():
if self.searchBox.hasFocus(): if self.searchBox.hasFocus():
self.replaceBox.setFocus(True) self.replaceBox.setFocus()
return True return True
elif self.replaceBox.hasFocus(): elif self.replaceBox.hasFocus():
self.searchBox.setFocus(True) self.searchBox.setFocus()
return True return True
return False return False
@@ -2498,12 +2526,14 @@ class GuiDocEditSearch(QFrame):
# Slots # Slots
## ##
@pyqtSlot()
def _doClose(self): def _doClose(self):
"""Hide the search/replace bar. """Hide the search/replace bar.
""" """
self.closeSearch() self.closeSearch()
return return
@pyqtSlot()
def _doSearch(self): def _doSearch(self):
"""Call the search action function for the document editor. """Call the search action function for the document editor.
""" """
@@ -2514,12 +2544,14 @@ class GuiDocEditSearch(QFrame):
self.docEditor.findNext() self.docEditor.findNext()
return return
@pyqtSlot()
def _doReplace(self): def _doReplace(self):
"""Call the replace action function for the document editor. """Call the replace action function for the document editor.
""" """
self.docEditor.replaceNext() self.docEditor.replaceNext()
return return
@pyqtSlot(bool)
def _doToggleReplace(self, theState): def _doToggleReplace(self, theState):
"""Toggle the show/hide of the replace box. """Toggle the show/hide of the replace box.
""" """
@@ -2534,36 +2566,42 @@ class GuiDocEditSearch(QFrame):
self.docEditor.updateDocMargins() self.docEditor.updateDocMargins()
return return
@pyqtSlot(bool)
def _doToggleCase(self, theState): def _doToggleCase(self, theState):
"""Enable/disable case sensitive mode. """Enable/disable case sensitive mode.
""" """
self.isCaseSense = theState self.isCaseSense = theState
return return
@pyqtSlot(bool)
def _doToggleWord(self, theState): def _doToggleWord(self, theState):
"""Enable/disable whole word search mode. """Enable/disable whole word search mode.
""" """
self.isWholeWord = theState self.isWholeWord = theState
return return
@pyqtSlot(bool)
def _doToggleRegEx(self, theState): def _doToggleRegEx(self, theState):
"""Enable/disable regular expression search mode. """Enable/disable regular expression search mode.
""" """
self.isRegEx = theState self.isRegEx = theState
return return
@pyqtSlot(bool)
def _doToggleLoop(self, theState): def _doToggleLoop(self, theState):
"""Enable/disable looping the search. """Enable/disable looping the search.
""" """
self.doLoop = theState self.doLoop = theState
return return
@pyqtSlot(bool)
def _doToggleProject(self, theState): def _doToggleProject(self, theState):
"""Enable/disable continuing search in next project file. """Enable/disable continuing search in next project file.
""" """
self.doNextFile = theState self.doNextFile = theState
return return
@pyqtSlot(bool)
def _doToggleMatchCap(self, theState): def _doToggleMatchCap(self, theState):
"""Enable/disable preserving capitalisation when replacing. """Enable/disable preserving capitalisation when replacing.
""" """
@@ -2626,51 +2664,38 @@ class GuiDocEditHeader(QWidget):
lblFont.setPointSizeF(0.9*self.mainTheme.fontPointSize) lblFont.setPointSizeF(0.9*self.mainTheme.fontPointSize)
self.theTitle.setFont(lblFont) self.theTitle.setFont(lblFont)
buttonStyle = (
"QToolButton {{border: none; background: transparent;}} "
"QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}"
).format(*self.mainTheme.colText)
# Buttons # Buttons
self.editButton = QToolButton(self) self.editButton = QToolButton(self)
self.editButton.setIcon(self.mainTheme.getIcon("edit"))
self.editButton.setContentsMargins(0, 0, 0, 0) self.editButton.setContentsMargins(0, 0, 0, 0)
self.editButton.setIconSize(QSize(fPx, fPx)) self.editButton.setIconSize(QSize(fPx, fPx))
self.editButton.setFixedSize(fPx, fPx) self.editButton.setFixedSize(fPx, fPx)
self.editButton.setStyleSheet(buttonStyle)
self.editButton.setToolButtonStyle(Qt.ToolButtonIconOnly) self.editButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
self.editButton.setVisible(False) self.editButton.setVisible(False)
self.editButton.setToolTip(self.tr("Edit document label")) self.editButton.setToolTip(self.tr("Edit document label"))
self.editButton.clicked.connect(self._editDocument) self.editButton.clicked.connect(self._editDocument)
self.searchButton = QToolButton(self) self.searchButton = QToolButton(self)
self.searchButton.setIcon(self.mainTheme.getIcon("search"))
self.searchButton.setContentsMargins(0, 0, 0, 0) self.searchButton.setContentsMargins(0, 0, 0, 0)
self.searchButton.setIconSize(QSize(fPx, fPx)) self.searchButton.setIconSize(QSize(fPx, fPx))
self.searchButton.setFixedSize(fPx, fPx) self.searchButton.setFixedSize(fPx, fPx)
self.searchButton.setStyleSheet(buttonStyle)
self.searchButton.setToolButtonStyle(Qt.ToolButtonIconOnly) self.searchButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
self.searchButton.setVisible(False) self.searchButton.setVisible(False)
self.searchButton.setToolTip(self.tr("Search document")) self.searchButton.setToolTip(self.tr("Search document"))
self.searchButton.clicked.connect(self._searchDocument) self.searchButton.clicked.connect(self._searchDocument)
self.minmaxButton = QToolButton(self) self.minmaxButton = QToolButton(self)
self.minmaxButton.setIcon(self.mainTheme.getIcon("maximise"))
self.minmaxButton.setContentsMargins(0, 0, 0, 0) self.minmaxButton.setContentsMargins(0, 0, 0, 0)
self.minmaxButton.setIconSize(QSize(fPx, fPx)) self.minmaxButton.setIconSize(QSize(fPx, fPx))
self.minmaxButton.setFixedSize(fPx, fPx) self.minmaxButton.setFixedSize(fPx, fPx)
self.minmaxButton.setStyleSheet(buttonStyle)
self.minmaxButton.setToolButtonStyle(Qt.ToolButtonIconOnly) self.minmaxButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
self.minmaxButton.setVisible(False) self.minmaxButton.setVisible(False)
self.minmaxButton.setToolTip(self.tr("Toggle Focus Mode")) self.minmaxButton.setToolTip(self.tr("Toggle Focus Mode"))
self.minmaxButton.clicked.connect(self._minmaxDocument) self.minmaxButton.clicked.connect(self._minmaxDocument)
self.closeButton = QToolButton(self) self.closeButton = QToolButton(self)
self.closeButton.setIcon(self.mainTheme.getIcon("close"))
self.closeButton.setContentsMargins(0, 0, 0, 0) self.closeButton.setContentsMargins(0, 0, 0, 0)
self.closeButton.setIconSize(QSize(fPx, fPx)) self.closeButton.setIconSize(QSize(fPx, fPx))
self.closeButton.setFixedSize(fPx, fPx) self.closeButton.setFixedSize(fPx, fPx)
self.closeButton.setStyleSheet(buttonStyle)
self.closeButton.setToolButtonStyle(Qt.ToolButtonIconOnly) self.closeButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
self.closeButton.setVisible(False) self.closeButton.setVisible(False)
self.closeButton.setToolTip(self.tr("Close the document")) self.closeButton.setToolTip(self.tr("Close the document"))
@@ -2693,8 +2718,7 @@ class GuiDocEditHeader(QWidget):
self.outerBox.setContentsMargins(cM, cM, cM, cM) self.outerBox.setContentsMargins(cM, cM, cM, cM)
self.setMinimumHeight(fPx + 2*cM) self.setMinimumHeight(fPx + 2*cM)
# Fix the Colours self.updateTheme()
self.matchColours()
logger.debug("GuiDocEditHeader initialisation complete") logger.debug("GuiDocEditHeader initialisation complete")
@@ -2704,6 +2728,28 @@ class GuiDocEditHeader(QWidget):
# Methods # Methods
## ##
def updateTheme(self):
"""Update theme elements.
"""
self.editButton.setIcon(self.mainTheme.getIcon("edit"))
self.searchButton.setIcon(self.mainTheme.getIcon("search"))
self.minmaxButton.setIcon(self.mainTheme.getIcon("maximise"))
self.closeButton.setIcon(self.mainTheme.getIcon("close"))
buttonStyle = (
"QToolButton {{border: none; background: transparent;}} "
"QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}"
).format(*self.mainTheme.colText)
self.editButton.setStyleSheet(buttonStyle)
self.searchButton.setStyleSheet(buttonStyle)
self.minmaxButton.setStyleSheet(buttonStyle)
self.closeButton.setStyleSheet(buttonStyle)
self.matchColours()
return
def matchColours(self): def matchColours(self):
"""Update the colours of the widget to match those of the syntax """Update the colours of the widget to match those of the syntax
theme rather than the main GUI. theme rather than the main GUI.
@@ -2768,18 +2814,21 @@ class GuiDocEditHeader(QWidget):
# Slots # Slots
## ##
@pyqtSlot()
def _editDocument(self): def _editDocument(self):
"""Open the edit item dialog from the main GUI. """Open the edit item dialog from the main GUI.
""" """
self.mainGui.editItemLabel(self._docHandle) self.mainGui.editItemLabel(self._docHandle)
return return
@pyqtSlot()
def _searchDocument(self): def _searchDocument(self):
"""Toggle the visibility of the search box. """Toggle the visibility of the search box.
""" """
self.docEditor.toggleSearch() self.docEditor.toggleSearch()
return return
@pyqtSlot()
def _closeDocument(self): def _closeDocument(self):
"""Trigger the close editor on the main window. """Trigger the close editor on the main window.
""" """
@@ -2790,6 +2839,7 @@ class GuiDocEditHeader(QWidget):
self.minmaxButton.setVisible(False) self.minmaxButton.setVisible(False)
return return
@pyqtSlot()
def _minmaxDocument(self): def _minmaxDocument(self):
"""Switch on or off Focus Mode. """Switch on or off Focus Mode.
""" """
@@ -2862,7 +2912,6 @@ class GuiDocEditFooter(QWidget):
# Lines # Lines
self.linesIcon = QLabel("") self.linesIcon = QLabel("")
self.linesIcon.setPixmap(self.mainTheme.getPixmap("status_lines", (self.sPx, self.sPx)))
self.linesIcon.setContentsMargins(0, 0, 0, 0) self.linesIcon.setContentsMargins(0, 0, 0, 0)
self.linesIcon.setFixedHeight(self.sPx) self.linesIcon.setFixedHeight(self.sPx)
self.linesIcon.setAlignment(Qt.AlignLeft | Qt.AlignTop) self.linesIcon.setAlignment(Qt.AlignLeft | Qt.AlignTop)
@@ -2878,7 +2927,6 @@ class GuiDocEditFooter(QWidget):
# Words # Words
self.wordsIcon = QLabel("") self.wordsIcon = QLabel("")
self.wordsIcon.setPixmap(self.mainTheme.getPixmap("status_stats", (self.sPx, self.sPx)))
self.wordsIcon.setContentsMargins(0, 0, 0, 0) self.wordsIcon.setContentsMargins(0, 0, 0, 0)
self.wordsIcon.setFixedHeight(self.sPx) self.wordsIcon.setFixedHeight(self.sPx)
self.wordsIcon.setAlignment(Qt.AlignLeft | Qt.AlignTop) self.wordsIcon.setAlignment(Qt.AlignLeft | Qt.AlignTop)
@@ -2913,7 +2961,7 @@ class GuiDocEditFooter(QWidget):
self.setMinimumHeight(fPx + 2*cM) self.setMinimumHeight(fPx + 2*cM)
# Fix the Colours # Fix the Colours
self.matchColours() self.updateTheme()
self.updateLineCount() self.updateLineCount()
self.updateCounts() self.updateCounts()
@@ -2925,6 +2973,16 @@ class GuiDocEditFooter(QWidget):
# Methods # Methods
## ##
def updateTheme(self):
"""Update theme elements.
"""
self.linesIcon.setPixmap(self.mainTheme.getPixmap("status_lines", (self.sPx, self.sPx)))
self.wordsIcon.setPixmap(self.mainTheme.getPixmap("status_stats", (self.sPx, self.sPx)))
self.matchColours()
return
def matchColours(self): def matchColours(self):
"""Update the colours of the widget to match those of the syntax """Update the colours of the widget to match those of the syntax
theme rather than the main GUI. theme rather than the main GUI.
+77 -41
View File
@@ -102,6 +102,13 @@ class GuiDocViewer(QTextBrowser):
self.docHeader.setTitleFromHandle(self._docHandle) self.docHeader.setTitleFromHandle(self._docHandle)
return True return True
def updateTheme(self):
"""Update theme elements.
"""
self.docHeader.updateTheme()
self.docFooter.updateTheme()
return
def initViewer(self): def initViewer(self):
"""Set editor settings from main config. """Set editor settings from main config.
""" """
@@ -735,51 +742,38 @@ class GuiDocViewHeader(QWidget):
lblFont.setPointSizeF(0.9*self.mainTheme.fontPointSize) lblFont.setPointSizeF(0.9*self.mainTheme.fontPointSize)
self.theTitle.setFont(lblFont) self.theTitle.setFont(lblFont)
buttonStyle = (
"QToolButton {{border: none; background: transparent;}} "
"QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}"
).format(*self.mainTheme.colText)
# Buttons # Buttons
self.backButton = QToolButton(self) self.backButton = QToolButton(self)
self.backButton.setIcon(self.mainTheme.getIcon("backward"))
self.backButton.setContentsMargins(0, 0, 0, 0) self.backButton.setContentsMargins(0, 0, 0, 0)
self.backButton.setIconSize(QSize(fPx, fPx)) self.backButton.setIconSize(QSize(fPx, fPx))
self.backButton.setFixedSize(fPx, fPx) self.backButton.setFixedSize(fPx, fPx)
self.backButton.setStyleSheet(buttonStyle)
self.backButton.setToolButtonStyle(Qt.ToolButtonIconOnly) self.backButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
self.backButton.setVisible(False) self.backButton.setVisible(False)
self.backButton.setToolTip(self.tr("Go backward")) self.backButton.setToolTip(self.tr("Go backward"))
self.backButton.clicked.connect(self.docViewer.navBackward) self.backButton.clicked.connect(self.docViewer.navBackward)
self.forwardButton = QToolButton(self) self.forwardButton = QToolButton(self)
self.forwardButton.setIcon(self.mainTheme.getIcon("forward"))
self.forwardButton.setContentsMargins(0, 0, 0, 0) self.forwardButton.setContentsMargins(0, 0, 0, 0)
self.forwardButton.setIconSize(QSize(fPx, fPx)) self.forwardButton.setIconSize(QSize(fPx, fPx))
self.forwardButton.setFixedSize(fPx, fPx) self.forwardButton.setFixedSize(fPx, fPx)
self.forwardButton.setStyleSheet(buttonStyle)
self.forwardButton.setToolButtonStyle(Qt.ToolButtonIconOnly) self.forwardButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
self.forwardButton.setVisible(False) self.forwardButton.setVisible(False)
self.forwardButton.setToolTip(self.tr("Go forward")) self.forwardButton.setToolTip(self.tr("Go forward"))
self.forwardButton.clicked.connect(self.docViewer.navForward) self.forwardButton.clicked.connect(self.docViewer.navForward)
self.refreshButton = QToolButton(self) self.refreshButton = QToolButton(self)
self.refreshButton.setIcon(self.mainTheme.getIcon("refresh"))
self.refreshButton.setContentsMargins(0, 0, 0, 0) self.refreshButton.setContentsMargins(0, 0, 0, 0)
self.refreshButton.setIconSize(QSize(fPx, fPx)) self.refreshButton.setIconSize(QSize(fPx, fPx))
self.refreshButton.setFixedSize(fPx, fPx) self.refreshButton.setFixedSize(fPx, fPx)
self.refreshButton.setStyleSheet(buttonStyle)
self.refreshButton.setToolButtonStyle(Qt.ToolButtonIconOnly) self.refreshButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
self.refreshButton.setVisible(False) self.refreshButton.setVisible(False)
self.refreshButton.setToolTip(self.tr("Reload the document")) self.refreshButton.setToolTip(self.tr("Reload the document"))
self.refreshButton.clicked.connect(self._refreshDocument) self.refreshButton.clicked.connect(self._refreshDocument)
self.closeButton = QToolButton(self) self.closeButton = QToolButton(self)
self.closeButton.setIcon(self.mainTheme.getIcon("close"))
self.closeButton.setContentsMargins(0, 0, 0, 0) self.closeButton.setContentsMargins(0, 0, 0, 0)
self.closeButton.setIconSize(QSize(fPx, fPx)) self.closeButton.setIconSize(QSize(fPx, fPx))
self.closeButton.setFixedSize(fPx, fPx) self.closeButton.setFixedSize(fPx, fPx)
self.closeButton.setStyleSheet(buttonStyle)
self.closeButton.setToolButtonStyle(Qt.ToolButtonIconOnly) self.closeButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
self.closeButton.setVisible(False) self.closeButton.setVisible(False)
self.closeButton.setToolTip(self.tr("Close the document")) self.closeButton.setToolTip(self.tr("Close the document"))
@@ -803,7 +797,7 @@ class GuiDocViewHeader(QWidget):
self.setMinimumHeight(fPx + 2*cM) self.setMinimumHeight(fPx + 2*cM)
# Fix the Colours # Fix the Colours
self.matchColours() self.updateTheme()
logger.debug("GuiDocViewHeader initialisation complete") logger.debug("GuiDocViewHeader initialisation complete")
@@ -813,6 +807,28 @@ class GuiDocViewHeader(QWidget):
# Methods # Methods
## ##
def updateTheme(self):
"""Update theme elements.
"""
self.backButton.setIcon(self.mainTheme.getIcon("backward"))
self.forwardButton.setIcon(self.mainTheme.getIcon("forward"))
self.refreshButton.setIcon(self.mainTheme.getIcon("refresh"))
self.closeButton.setIcon(self.mainTheme.getIcon("close"))
buttonStyle = (
"QToolButton {{border: none; background: transparent;}} "
"QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}"
).format(*self.mainTheme.colText)
self.backButton.setStyleSheet(buttonStyle)
self.forwardButton.setStyleSheet(buttonStyle)
self.refreshButton.setStyleSheet(buttonStyle)
self.closeButton.setStyleSheet(buttonStyle)
self.matchColours()
return
def matchColours(self): def matchColours(self):
"""Update the colours of the widget to match those of the syntax """Update the colours of the widget to match those of the syntax
theme rather than the main GUI. theme rather than the main GUI.
@@ -873,12 +889,14 @@ class GuiDocViewHeader(QWidget):
# Slots # Slots
## ##
@pyqtSlot()
def _closeDocument(self): def _closeDocument(self):
"""Trigger the close editor/viewer on the main window. """Trigger the close editor/viewer on the main window.
""" """
self.mainGui.closeDocViewer() self.mainGui.closeDocViewer()
return return
@pyqtSlot()
def _refreshDocument(self): def _refreshDocument(self):
"""Reload the content of the document. """Reload the content of the document.
""" """
@@ -926,33 +944,13 @@ class GuiDocViewFooter(QWidget):
bSp = self.mainConf.pxInt(2) bSp = self.mainConf.pxInt(2)
hSp = self.mainConf.pxInt(8) hSp = self.mainConf.pxInt(8)
# Icons
stickyOn = self.mainTheme.getPixmap("sticky-on", (fPx, fPx))
stickyOff = self.mainTheme.getPixmap("sticky-off", (fPx, fPx))
stickyIcon = QIcon()
stickyIcon.addPixmap(stickyOn, QIcon.Normal, QIcon.On)
stickyIcon.addPixmap(stickyOff, QIcon.Normal, QIcon.Off)
bulletOn = self.mainTheme.getPixmap("bullet-on", (fPx, fPx))
bulletOff = self.mainTheme.getPixmap("bullet-off", (fPx, fPx))
bulletIcon = QIcon()
bulletIcon.addPixmap(bulletOn, QIcon.Normal, QIcon.On)
bulletIcon.addPixmap(bulletOff, QIcon.Normal, QIcon.Off)
# Main Widget Settings # Main Widget Settings
self.setContentsMargins(0, 0, 0, 0) self.setContentsMargins(0, 0, 0, 0)
self.setAutoFillBackground(True) self.setAutoFillBackground(True)
buttonStyle = (
"QToolButton {{border: none; background: transparent;}} "
"QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}"
).format(*self.mainTheme.colText)
# Show/Hide Details # Show/Hide Details
self.showHide = QToolButton(self) self.showHide = QToolButton(self)
self.showHide.setToolButtonStyle(Qt.ToolButtonIconOnly) self.showHide.setToolButtonStyle(Qt.ToolButtonIconOnly)
self.showHide.setStyleSheet(buttonStyle)
self.showHide.setIcon(self.mainTheme.getIcon("reference"))
self.showHide.setIconSize(QSize(fPx, fPx)) self.showHide.setIconSize(QSize(fPx, fPx))
self.showHide.setFixedSize(QSize(fPx, fPx)) self.showHide.setFixedSize(QSize(fPx, fPx))
self.showHide.clicked.connect(self._doShowHide) self.showHide.clicked.connect(self._doShowHide)
@@ -962,8 +960,6 @@ class GuiDocViewFooter(QWidget):
self.stickyRefs = QToolButton(self) self.stickyRefs = QToolButton(self)
self.stickyRefs.setCheckable(True) self.stickyRefs.setCheckable(True)
self.stickyRefs.setToolButtonStyle(Qt.ToolButtonIconOnly) self.stickyRefs.setToolButtonStyle(Qt.ToolButtonIconOnly)
self.stickyRefs.setStyleSheet(buttonStyle)
self.stickyRefs.setIcon(stickyIcon)
self.stickyRefs.setIconSize(QSize(fPx, fPx)) self.stickyRefs.setIconSize(QSize(fPx, fPx))
self.stickyRefs.setFixedSize(QSize(fPx, fPx)) self.stickyRefs.setFixedSize(QSize(fPx, fPx))
self.stickyRefs.toggled.connect(self._doToggleSticky) self.stickyRefs.toggled.connect(self._doToggleSticky)
@@ -976,8 +972,6 @@ class GuiDocViewFooter(QWidget):
self.showComments.setCheckable(True) self.showComments.setCheckable(True)
self.showComments.setChecked(self.mainConf.viewComments) self.showComments.setChecked(self.mainConf.viewComments)
self.showComments.setToolButtonStyle(Qt.ToolButtonIconOnly) self.showComments.setToolButtonStyle(Qt.ToolButtonIconOnly)
self.showComments.setStyleSheet(buttonStyle)
self.showComments.setIcon(bulletIcon)
self.showComments.setIconSize(QSize(fPx, fPx)) self.showComments.setIconSize(QSize(fPx, fPx))
self.showComments.setFixedSize(QSize(fPx, fPx)) self.showComments.setFixedSize(QSize(fPx, fPx))
self.showComments.toggled.connect(self._doToggleComments) self.showComments.toggled.connect(self._doToggleComments)
@@ -988,8 +982,6 @@ class GuiDocViewFooter(QWidget):
self.showSynopsis.setCheckable(True) self.showSynopsis.setCheckable(True)
self.showSynopsis.setChecked(self.mainConf.viewSynopsis) self.showSynopsis.setChecked(self.mainConf.viewSynopsis)
self.showSynopsis.setToolButtonStyle(Qt.ToolButtonIconOnly) self.showSynopsis.setToolButtonStyle(Qt.ToolButtonIconOnly)
self.showSynopsis.setStyleSheet(buttonStyle)
self.showSynopsis.setIcon(bulletIcon)
self.showSynopsis.setIconSize(QSize(fPx, fPx)) self.showSynopsis.setIconSize(QSize(fPx, fPx))
self.showSynopsis.setFixedSize(QSize(fPx, fPx)) self.showSynopsis.setFixedSize(QSize(fPx, fPx))
self.showSynopsis.toggled.connect(self._doToggleSynopsis) self.showSynopsis.toggled.connect(self._doToggleSynopsis)
@@ -1063,7 +1055,7 @@ class GuiDocViewFooter(QWidget):
self.setMinimumHeight(fPx + 2*cM) self.setMinimumHeight(fPx + 2*cM)
# Fix the Colours # Fix the Colours
self.matchColours() self.updateTheme()
logger.debug("GuiDocViewFooter initialisation complete") logger.debug("GuiDocViewFooter initialisation complete")
@@ -1073,6 +1065,46 @@ class GuiDocViewFooter(QWidget):
# Methods # Methods
## ##
def updateTheme(self):
"""Update theme elements.
"""
# Icons
fPx = int(0.9*self.mainTheme.fontPixelSize)
stickyOn = self.mainTheme.getPixmap("sticky-on", (fPx, fPx))
stickyOff = self.mainTheme.getPixmap("sticky-off", (fPx, fPx))
stickyIcon = QIcon()
stickyIcon.addPixmap(stickyOn, QIcon.Normal, QIcon.On)
stickyIcon.addPixmap(stickyOff, QIcon.Normal, QIcon.Off)
bulletOn = self.mainTheme.getPixmap("bullet-on", (fPx, fPx))
bulletOff = self.mainTheme.getPixmap("bullet-off", (fPx, fPx))
bulletIcon = QIcon()
bulletIcon.addPixmap(bulletOn, QIcon.Normal, QIcon.On)
bulletIcon.addPixmap(bulletOff, QIcon.Normal, QIcon.Off)
self.showHide.setIcon(self.mainTheme.getIcon("reference"))
self.stickyRefs.setIcon(stickyIcon)
self.showComments.setIcon(bulletIcon)
self.showSynopsis.setIcon(bulletIcon)
# StyleSheets
buttonStyle = (
"QToolButton {{border: none; background: transparent;}} "
"QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}"
).format(*self.mainTheme.colText)
self.showHide.setStyleSheet(buttonStyle)
self.stickyRefs.setStyleSheet(buttonStyle)
self.showComments.setStyleSheet(buttonStyle)
self.showSynopsis.setStyleSheet(buttonStyle)
self.matchColours()
return
def matchColours(self): def matchColours(self):
"""Update the colours of the widget to match those of the syntax """Update the colours of the widget to match those of the syntax
theme rather than the main GUI. theme rather than the main GUI.
@@ -1094,6 +1126,7 @@ class GuiDocViewFooter(QWidget):
# Slots # Slots
## ##
@pyqtSlot()
def _doShowHide(self): def _doShowHide(self):
"""Toggle the expand/collapse of the panel. """Toggle the expand/collapse of the panel.
""" """
@@ -1101,6 +1134,7 @@ class GuiDocViewFooter(QWidget):
self.viewMeta.setVisible(not isVisible) self.viewMeta.setVisible(not isVisible)
return return
@pyqtSlot(bool)
def _doToggleSticky(self, theState): def _doToggleSticky(self, theState):
"""Toggle the sticky flag for the reference panel. """Toggle the sticky flag for the reference panel.
""" """
@@ -1110,6 +1144,7 @@ class GuiDocViewFooter(QWidget):
self.viewMeta.refreshReferences(self.docViewer.docHandle()) self.viewMeta.refreshReferences(self.docViewer.docHandle())
return return
@pyqtSlot(bool)
def _doToggleComments(self, theState): def _doToggleComments(self, theState):
"""Toggle the view comment button and reload the document. """Toggle the view comment button and reload the document.
""" """
@@ -1117,6 +1152,7 @@ class GuiDocViewFooter(QWidget):
self.docViewer.reloadText() self.docViewer.reloadText()
return return
@pyqtSlot(bool)
def _doToggleSynopsis(self, theState): def _doToggleSynopsis(self, theState):
"""Toggle the view synopsis button and reload the document. """Toggle the view synopsis button and reload the document.
""" """
+11 -4
View File
@@ -53,12 +53,8 @@ class GuiItemDetails(QWidget):
hSp = self.mainConf.pxInt(6) hSp = self.mainConf.pxInt(6)
vSp = self.mainConf.pxInt(1) vSp = self.mainConf.pxInt(1)
mPx = self.mainConf.pxInt(6) mPx = self.mainConf.pxInt(6)
iPx = self.mainTheme.baseIconSize
fPt = self.mainTheme.fontPointSize fPt = self.mainTheme.fontPointSize
self._expCheck = self.mainTheme.getPixmap("check", (iPx, iPx))
self._expCross = self.mainTheme.getPixmap("cross", (iPx, iPx))
fntLabel = QFont() fntLabel = QFont()
fntLabel.setBold(True) fntLabel.setBold(True)
fntLabel.setPointSizeF(0.9*fPt) fntLabel.setPointSizeF(0.9*fPt)
@@ -179,6 +175,8 @@ class GuiItemDetails(QWidget):
self.setLayout(self.mainBox) self.setLayout(self.mainBox)
self.updateTheme()
# Make sure the columns for flags and counts don't resize too often # Make sure the columns for flags and counts don't resize too often
flagWidth = self.mainTheme.getTextWidth("Mm", fntValue) flagWidth = self.mainTheme.getTextWidth("Mm", fntValue)
countWidth = self.mainTheme.getTextWidth("99,999", fntValue) countWidth = self.mainTheme.getTextWidth("99,999", fntValue)
@@ -219,6 +217,15 @@ class GuiItemDetails(QWidget):
""" """
self.updateViewBox(self._itemHandle) self.updateViewBox(self._itemHandle)
def updateTheme(self):
"""Update theme elements.
"""
iPx = self.mainTheme.baseIconSize
self._expCheck = self.mainTheme.getPixmap("check", (iPx, iPx))
self._expCross = self.mainTheme.getPixmap("cross", (iPx, iPx))
self.updateViewBox(self._itemHandle)
return
## ##
# Public Slots # Public Slots
## ##
+43 -17
View File
@@ -92,6 +92,14 @@ class GuiNovelView(QWidget):
# Methods # Methods
## ##
def updateTheme(self):
"""Update theme elements.
"""
self.novelBar.updateTheme()
self.novelTree.updateTheme()
self.refreshTree()
return
def initSettings(self): def initSettings(self):
"""Initialise GUI elements that depend on specific settings. """Initialise GUI elements that depend on specific settings.
""" """
@@ -181,16 +189,6 @@ class GuiNovelToolBar(QWidget):
self.setContentsMargins(0, 0, 0, 0) self.setContentsMargins(0, 0, 0, 0)
self.setAutoFillBackground(True) self.setAutoFillBackground(True)
qPalette = self.palette()
qPalette.setBrush(QPalette.Window, qPalette.base())
self.setPalette(qPalette)
fadeCol = qPalette.text().color()
buttonStyle = (
"QToolButton {{padding: {0}px; border: none; background: transparent;}} "
"QToolButton:hover {{border: none; background: rgba({1},{2},{3},0.2);}}"
).format(mPx, fadeCol.red(), fadeCol.green(), fadeCol.blue())
# Widget Label # Widget Label
self.viewLabel = QLabel("<b>%s</b>" % self.tr("Novel Outline")) self.viewLabel = QLabel("<b>%s</b>" % self.tr("Novel Outline"))
self.viewLabel.setContentsMargins(0, 0, 0, 0) self.viewLabel.setContentsMargins(0, 0, 0, 0)
@@ -199,9 +197,7 @@ class GuiNovelToolBar(QWidget):
# Refresh Button # Refresh Button
self.tbRefresh = QToolButton(self) self.tbRefresh = QToolButton(self)
self.tbRefresh.setToolTip(self.tr("Refresh")) self.tbRefresh.setToolTip(self.tr("Refresh"))
self.tbRefresh.setIcon(self.mainTheme.getIcon("refresh"))
self.tbRefresh.setIconSize(QSize(iPx, iPx)) self.tbRefresh.setIconSize(QSize(iPx, iPx))
self.tbRefresh.setStyleSheet(buttonStyle)
self.tbRefresh.clicked.connect(self._refreshNovelTree) self.tbRefresh.clicked.connect(self._refreshNovelTree)
# Novel Root Menu # Novel Root Menu
@@ -211,9 +207,7 @@ class GuiNovelToolBar(QWidget):
self.tbRoot = QToolButton(self) self.tbRoot = QToolButton(self)
self.tbRoot.setToolTip(self.tr("Novel Root")) self.tbRoot.setToolTip(self.tr("Novel Root"))
self.tbRoot.setIcon(self.mainTheme.getIcon(nwLabels.CLASS_ICON[nwItemClass.NOVEL]))
self.tbRoot.setIconSize(QSize(iPx, iPx)) self.tbRoot.setIconSize(QSize(iPx, iPx))
self.tbRoot.setStyleSheet(buttonStyle)
self.tbRoot.setMenu(self.mRoot) self.tbRoot.setMenu(self.mRoot)
self.tbRoot.setPopupMode(QToolButton.InstantPopup) self.tbRoot.setPopupMode(QToolButton.InstantPopup)
@@ -230,9 +224,7 @@ class GuiNovelToolBar(QWidget):
self.tbMore = QToolButton(self) self.tbMore = QToolButton(self)
self.tbMore.setToolTip(self.tr("More Options")) self.tbMore.setToolTip(self.tr("More Options"))
self.tbMore.setIcon(self.mainTheme.getIcon("menu"))
self.tbMore.setIconSize(QSize(iPx, iPx)) self.tbMore.setIconSize(QSize(iPx, iPx))
self.tbMore.setStyleSheet(buttonStyle)
self.tbMore.setMenu(self.mMore) self.tbMore.setMenu(self.mMore)
self.tbMore.setPopupMode(QToolButton.InstantPopup) self.tbMore.setPopupMode(QToolButton.InstantPopup)
@@ -247,6 +239,8 @@ class GuiNovelToolBar(QWidget):
self.setLayout(self.outerBox) self.setLayout(self.outerBox)
self.updateTheme()
logger.debug("GuiNovelToolBar initialisation complete") logger.debug("GuiNovelToolBar initialisation complete")
return return
@@ -255,6 +249,31 @@ class GuiNovelToolBar(QWidget):
# Methods # Methods
## ##
def updateTheme(self):
"""Update theme elements.
"""
# Icons
self.tbRefresh.setIcon(self.mainTheme.getIcon("refresh"))
self.tbRoot.setIcon(self.mainTheme.getIcon(nwLabels.CLASS_ICON[nwItemClass.NOVEL]))
self.tbMore.setIcon(self.mainTheme.getIcon("menu"))
qPalette = self.palette()
qPalette.setBrush(QPalette.Window, qPalette.base())
self.setPalette(qPalette)
# StyleSheets
fadeCol = qPalette.text().color()
buttonStyle = (
"QToolButton {{padding: {0}px; border: none; background: transparent;}} "
"QToolButton:hover {{border: none; background: rgba({1},{2},{3},0.2);}}"
).format(self.mainConf.pxInt(2), fadeCol.red(), fadeCol.green(), fadeCol.blue())
self.tbRefresh.setStyleSheet(buttonStyle)
self.tbRoot.setStyleSheet(buttonStyle)
self.tbMore.setStyleSheet(buttonStyle)
return
def clearContent(self): def clearContent(self):
"""Run clearing project tasks. """Run clearing project tasks.
""" """
@@ -390,7 +409,6 @@ class GuiNovelTree(QTreeWidget):
fH2.setBold(True) fH2.setBold(True)
self._hFonts = [self.font(), fH1, fH2, self.font(), self.font()] self._hFonts = [self.font(), fH1, fH2, self.font(), self.font()]
self._pMore = self.mainTheme.loadDecoration("deco_doc_more", pxH=iPx)
# Connect signals # Connect signals
self.clicked.connect(self._treeItemClicked) self.clicked.connect(self._treeItemClicked)
@@ -399,6 +417,7 @@ class GuiNovelTree(QTreeWidget):
# Set custom settings # Set custom settings
self.initSettings() self.initSettings()
self.updateTheme()
logger.debug("GuiNovelTree initialisation complete") logger.debug("GuiNovelTree initialisation complete")
@@ -420,6 +439,13 @@ class GuiNovelTree(QTreeWidget):
return return
def updateTheme(self):
"""Update theme elements.
"""
iPx = self.mainTheme.baseIconSize
self._pMore = self.mainTheme.loadDecoration("deco_doc_more", pxH=iPx)
return
## ##
# Properties # Properties
## ##
+17 -3
View File
@@ -97,6 +97,13 @@ class GuiOutlineView(QWidget):
# Methods # Methods
## ##
def updateTheme(self):
"""Update theme elements.
"""
self.outlineBar.updateTheme()
self.refreshTree()
return
def initSettings(self): def initSettings(self):
"""Initialise GUI elements that depend on specific settings. """Initialise GUI elements that depend on specific settings.
""" """
@@ -213,7 +220,6 @@ class GuiOutlineToolBar(QToolBar):
self.setMovable(False) self.setMovable(False)
self.setIconSize(QSize(iPx, iPx)) self.setIconSize(QSize(iPx, iPx))
self.setContentsMargins(0, 0, 0, 0) self.setContentsMargins(0, 0, 0, 0)
self.setStyleSheet("QToolBar {border: 0px;}")
stretch = QWidget(self) stretch = QWidget(self)
stretch.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) stretch.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
@@ -228,7 +234,6 @@ class GuiOutlineToolBar(QToolBar):
# Actions # Actions
self.aRefresh = QAction(self.tr("Refresh"), self) self.aRefresh = QAction(self.tr("Refresh"), self)
self.aRefresh.setIcon(self.mainTheme.getIcon("refresh"))
self.aRefresh.triggered.connect(self._refreshRequested) self.aRefresh.triggered.connect(self._refreshRequested)
# Column Menu # Column Menu
@@ -238,7 +243,6 @@ class GuiOutlineToolBar(QToolBar):
) )
self.tbColumns = QToolButton(self) self.tbColumns = QToolButton(self)
self.tbColumns.setIcon(self.mainTheme.getIcon("menu"))
self.tbColumns.setMenu(self.mColumns) self.tbColumns.setMenu(self.mColumns)
self.tbColumns.setPopupMode(QToolButton.InstantPopup) self.tbColumns.setPopupMode(QToolButton.InstantPopup)
@@ -258,6 +262,16 @@ class GuiOutlineToolBar(QToolBar):
# Methods # Methods
## ##
def updateTheme(self):
"""Update theme elements.
"""
self.setStyleSheet("QToolBar {border: 0px;}")
self.aRefresh.setIcon(self.mainTheme.getIcon("refresh"))
self.tbColumns.setIcon(self.mainTheme.getIcon("menu"))
return
def populateNovelList(self): def populateNovelList(self):
"""Fill the novel combo box with a list of all novel folders. """Fill the novel combo box with a list of all novel folders.
""" """
+66 -41
View File
@@ -119,6 +119,13 @@ class GuiProjectView(QWidget):
# Methods # Methods
## ##
def updateTheme(self):
"""Update theme elements.
"""
self.projBar.updateTheme()
self.populateTree()
return
def initSettings(self): def initSettings(self):
"""Initialise GUI elements that depend on specific settings. """Initialise GUI elements that depend on specific settings.
""" """
@@ -213,16 +220,6 @@ class GuiProjectToolBar(QWidget):
self.setContentsMargins(0, 0, 0, 0) self.setContentsMargins(0, 0, 0, 0)
self.setAutoFillBackground(True) self.setAutoFillBackground(True)
qPalette = self.palette()
qPalette.setBrush(QPalette.Window, qPalette.base())
self.setPalette(qPalette)
fadeCol = qPalette.text().color()
buttonStyle = (
"QToolButton {{padding: {0}px; border: none; background: transparent;}} "
"QToolButton:hover {{border: none; background: rgba({1},{2},{3},0.2);}}"
).format(mPx, fadeCol.red(), fadeCol.green(), fadeCol.blue())
# Widget Label # Widget Label
self.viewLabel = QLabel("<b>%s</b>" % self.tr("Project Content")) self.viewLabel = QLabel("<b>%s</b>" % self.tr("Project Content"))
self.viewLabel.setContentsMargins(0, 0, 0, 0) self.viewLabel.setContentsMargins(0, 0, 0, 0)
@@ -234,78 +231,56 @@ class GuiProjectToolBar(QWidget):
self.tbQuick = QToolButton(self) self.tbQuick = QToolButton(self)
self.tbQuick.setToolTip("%s [Ctrl+L]" % self.tr("Quick Links")) self.tbQuick.setToolTip("%s [Ctrl+L]" % self.tr("Quick Links"))
self.tbQuick.setShortcut("Ctrl+L") self.tbQuick.setShortcut("Ctrl+L")
self.tbQuick.setIcon(self.mainTheme.getIcon("bookmark"))
self.tbQuick.setIconSize(QSize(iPx, iPx)) self.tbQuick.setIconSize(QSize(iPx, iPx))
self.tbQuick.setStyleSheet(buttonStyle)
self.tbQuick.setMenu(self.mQuick) self.tbQuick.setMenu(self.mQuick)
self.tbQuick.setPopupMode(QToolButton.InstantPopup) self.tbQuick.setPopupMode(QToolButton.InstantPopup)
# Move Buttons # Move Buttons
self.tbMoveU = QToolButton(self) self.tbMoveU = QToolButton(self)
self.tbMoveU.setToolTip("%s [Ctrl+Up]" % self.tr("Move Up")) self.tbMoveU.setToolTip("%s [Ctrl+Up]" % self.tr("Move Up"))
self.tbMoveU.setIcon(self.mainTheme.getIcon("up"))
self.tbMoveU.setIconSize(QSize(iPx, iPx)) self.tbMoveU.setIconSize(QSize(iPx, iPx))
self.tbMoveU.setStyleSheet(buttonStyle)
self.tbMoveU.clicked.connect(lambda: self.projTree.moveTreeItem(-1)) self.tbMoveU.clicked.connect(lambda: self.projTree.moveTreeItem(-1))
self.tbMoveD = QToolButton(self) self.tbMoveD = QToolButton(self)
self.tbMoveD.setToolTip("%s [Ctrl+Down]" % self.tr("Move Down")) self.tbMoveD.setToolTip("%s [Ctrl+Down]" % self.tr("Move Down"))
self.tbMoveD.setIcon(self.mainTheme.getIcon("down"))
self.tbMoveD.setIconSize(QSize(iPx, iPx)) self.tbMoveD.setIconSize(QSize(iPx, iPx))
self.tbMoveD.setStyleSheet(buttonStyle)
self.tbMoveD.clicked.connect(lambda: self.projTree.moveTreeItem(1)) self.tbMoveD.clicked.connect(lambda: self.projTree.moveTreeItem(1))
# Add Item Menu # Add Item Menu
self.mAdd = QMenu() self.mAdd = QMenu()
self.aAddEmpty = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["document"])) self.aAddEmpty = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["document"]))
self.aAddEmpty.setIcon(self.mainTheme.getIcon("proj_document"))
self.aAddEmpty.triggered.connect( self.aAddEmpty.triggered.connect(
lambda: self.projTree.newTreeItem(nwItemType.FILE, hLevel=0, isNote=False) lambda: self.projTree.newTreeItem(nwItemType.FILE, hLevel=0, isNote=False)
) )
self.aAddChap = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["doc_h2"])) self.aAddChap = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["doc_h2"]))
self.aAddChap.setIcon(self.mainTheme.getIcon("proj_chapter"))
self.aAddChap.triggered.connect( self.aAddChap.triggered.connect(
lambda: self.projTree.newTreeItem(nwItemType.FILE, hLevel=2, isNote=False) lambda: self.projTree.newTreeItem(nwItemType.FILE, hLevel=2, isNote=False)
) )
self.aAddScene = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["doc_h3"])) self.aAddScene = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["doc_h3"]))
self.aAddScene.setIcon(self.mainTheme.getIcon("proj_scene"))
self.aAddScene.triggered.connect( self.aAddScene.triggered.connect(
lambda: self.projTree.newTreeItem(nwItemType.FILE, hLevel=3, isNote=False) lambda: self.projTree.newTreeItem(nwItemType.FILE, hLevel=3, isNote=False)
) )
self.aAddNote = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["note"])) self.aAddNote = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["note"]))
self.aAddNote.setIcon(self.mainTheme.getIcon("proj_note"))
self.aAddNote.triggered.connect( self.aAddNote.triggered.connect(
lambda: self.projTree.newTreeItem(nwItemType.FILE, hLevel=1, isNote=True) lambda: self.projTree.newTreeItem(nwItemType.FILE, hLevel=1, isNote=True)
) )
self.aAddFolder = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["folder"])) self.aAddFolder = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["folder"]))
self.aAddFolder.setIcon(self.mainTheme.getIcon("proj_folder"))
self.aAddFolder.triggered.connect( self.aAddFolder.triggered.connect(
lambda: self.projTree.newTreeItem(nwItemType.FOLDER) lambda: self.projTree.newTreeItem(nwItemType.FOLDER)
) )
self.mAddRoot = self.mAdd.addMenu(trConst(nwLabels.ITEM_DESCRIPTION["root"])) self.mAddRoot = self.mAdd.addMenu(trConst(nwLabels.ITEM_DESCRIPTION["root"]))
self._addRootFolderEntry(nwItemClass.NOVEL) self._buildRootMenu()
self._addRootFolderEntry(nwItemClass.ARCHIVE)
self.mAddRoot.addSeparator()
self._addRootFolderEntry(nwItemClass.PLOT)
self._addRootFolderEntry(nwItemClass.CHARACTER)
self._addRootFolderEntry(nwItemClass.WORLD)
self._addRootFolderEntry(nwItemClass.TIMELINE)
self._addRootFolderEntry(nwItemClass.OBJECT)
self._addRootFolderEntry(nwItemClass.ENTITY)
self._addRootFolderEntry(nwItemClass.CUSTOM)
self.tbAdd = QToolButton(self) self.tbAdd = QToolButton(self)
self.tbAdd.setToolTip("%s [Ctrl+N]" % self.tr("Add Item")) self.tbAdd.setToolTip("%s [Ctrl+N]" % self.tr("Add Item"))
self.tbAdd.setShortcut("Ctrl+N") self.tbAdd.setShortcut("Ctrl+N")
self.tbAdd.setIcon(self.mainTheme.getIcon("add"))
self.tbAdd.setIconSize(QSize(iPx, iPx)) self.tbAdd.setIconSize(QSize(iPx, iPx))
self.tbAdd.setStyleSheet(buttonStyle)
self.tbAdd.setMenu(self.mAdd) self.tbAdd.setMenu(self.mAdd)
self.tbAdd.setPopupMode(QToolButton.InstantPopup) self.tbAdd.setPopupMode(QToolButton.InstantPopup)
@@ -326,9 +301,7 @@ class GuiProjectToolBar(QWidget):
self.tbMore = QToolButton(self) self.tbMore = QToolButton(self)
self.tbMore.setToolTip(self.tr("More Options")) self.tbMore.setToolTip(self.tr("More Options"))
self.tbMore.setIcon(self.mainTheme.getIcon("menu"))
self.tbMore.setIconSize(QSize(iPx, iPx)) self.tbMore.setIconSize(QSize(iPx, iPx))
self.tbMore.setStyleSheet(buttonStyle)
self.tbMore.setMenu(self.mMore) self.tbMore.setMenu(self.mMore)
self.tbMore.setPopupMode(QToolButton.InstantPopup) self.tbMore.setPopupMode(QToolButton.InstantPopup)
@@ -344,6 +317,7 @@ class GuiProjectToolBar(QWidget):
self.outerBox.setSpacing(0) self.outerBox.setSpacing(0)
self.setLayout(self.outerBox) self.setLayout(self.outerBox)
self.updateTheme()
logger.debug("GuiProjectToolBar initialisation complete") logger.debug("GuiProjectToolBar initialisation complete")
@@ -353,6 +327,41 @@ class GuiProjectToolBar(QWidget):
# Methods # Methods
## ##
def updateTheme(self):
"""Update theme elements.
"""
qPalette = self.palette()
qPalette.setBrush(QPalette.Window, qPalette.base())
self.setPalette(qPalette)
fadeCol = qPalette.text().color()
buttonStyle = (
"QToolButton {{padding: {0}px; border: none; background: transparent;}} "
"QToolButton:hover {{border: none; background: rgba({1},{2},{3},0.2);}}"
).format(self.mainConf.pxInt(2), fadeCol.red(), fadeCol.green(), fadeCol.blue())
self.tbQuick.setStyleSheet(buttonStyle)
self.tbMoveU.setStyleSheet(buttonStyle)
self.tbMoveD.setStyleSheet(buttonStyle)
self.tbAdd.setStyleSheet(buttonStyle)
self.tbMore.setStyleSheet(buttonStyle)
self.tbQuick.setIcon(self.mainTheme.getIcon("bookmark"))
self.tbMoveU.setIcon(self.mainTheme.getIcon("up"))
self.tbMoveD.setIcon(self.mainTheme.getIcon("down"))
self.aAddEmpty.setIcon(self.mainTheme.getIcon("proj_document"))
self.aAddChap.setIcon(self.mainTheme.getIcon("proj_chapter"))
self.aAddScene.setIcon(self.mainTheme.getIcon("proj_scene"))
self.aAddNote.setIcon(self.mainTheme.getIcon("proj_note"))
self.aAddFolder.setIcon(self.mainTheme.getIcon("proj_folder"))
self.tbAdd.setIcon(self.mainTheme.getIcon("add"))
self.tbMore.setIcon(self.mainTheme.getIcon("menu"))
self.buildQuickLinkMenu()
self._buildRootMenu()
return
def clearContent(self): def clearContent(self):
"""Clear dynamic content on the tool bar. """Clear dynamic content on the tool bar.
""" """
@@ -379,13 +388,29 @@ class GuiProjectToolBar(QWidget):
# Internal Functions # Internal Functions
## ##
def _addRootFolderEntry(self, itemClass): def _buildRootMenu(self):
"""Add a menu entry for a root folder of a given class. """Build the rood folder menu.
""" """
aNew = self.mAddRoot.addAction(trConst(nwLabels.CLASS_NAME[itemClass])) def addClass(itemClass):
aNew.setIcon(self.mainTheme.getIcon(nwLabels.CLASS_ICON[itemClass])) aNew = self.mAddRoot.addAction(trConst(nwLabels.CLASS_NAME[itemClass]))
aNew.triggered.connect(lambda: self.projTree.newTreeItem(nwItemType.ROOT, itemClass)) aNew.setIcon(self.mainTheme.getIcon(nwLabels.CLASS_ICON[itemClass]))
self.mAddRoot.addAction(aNew) aNew.triggered.connect(lambda: self.projTree.newTreeItem(nwItemType.ROOT, itemClass))
self.mAddRoot.addAction(aNew)
return
self.mAddRoot.clear()
addClass(nwItemClass.NOVEL)
addClass(nwItemClass.ARCHIVE)
self.mAddRoot.addSeparator()
addClass(nwItemClass.PLOT)
addClass(nwItemClass.CHARACTER)
addClass(nwItemClass.WORLD)
addClass(nwItemClass.TIMELINE)
addClass(nwItemClass.OBJECT)
addClass(nwItemClass.ENTITY)
addClass(nwItemClass.CUSTOM)
return
# END Class GuiProjectToolBar # END Class GuiProjectToolBar
+16 -6
View File
@@ -66,7 +66,6 @@ class GuiMainStatus(QStatusBar):
# The Spell Checker Language # The Spell Checker Language
self.langIcon = QLabel("") self.langIcon = QLabel("")
self.langText = QLabel(self.tr("None")) self.langText = QLabel(self.tr("None"))
self.langIcon.setPixmap(self.mainTheme.getPixmap("status_lang", (iPx, iPx)))
self.langIcon.setContentsMargins(0, 0, 0, 0) self.langIcon.setContentsMargins(0, 0, 0, 0)
self.langText.setContentsMargins(0, 0, xM, 0) self.langText.setContentsMargins(0, 0, xM, 0)
self.addPermanentWidget(self.langIcon) self.addPermanentWidget(self.langIcon)
@@ -91,7 +90,6 @@ class GuiMainStatus(QStatusBar):
# The Project and Session Stats # The Project and Session Stats
self.statsIcon = QLabel() self.statsIcon = QLabel()
self.statsText = QLabel("") self.statsText = QLabel("")
self.statsIcon.setPixmap(self.mainTheme.getPixmap("status_stats", (iPx, iPx)))
self.statsIcon.setContentsMargins(0, 0, 0, 0) self.statsIcon.setContentsMargins(0, 0, 0, 0)
self.statsText.setContentsMargins(0, 0, xM, 0) self.statsText.setContentsMargins(0, 0, xM, 0)
self.addPermanentWidget(self.statsIcon) self.addPermanentWidget(self.statsIcon)
@@ -99,12 +97,8 @@ class GuiMainStatus(QStatusBar):
# The Session Clock # The Session Clock
# Set the mimimum width so the label doesn't rescale every second # Set the mimimum width so the label doesn't rescale every second
self.timePixmap = self.mainTheme.getPixmap("status_time", (iPx, iPx))
self.idlePixmap = self.mainTheme.getPixmap("status_idle", (iPx, iPx))
self.timeIcon = QLabel() self.timeIcon = QLabel()
self.timeText = QLabel("") self.timeText = QLabel("")
self.timeIcon.setPixmap(self.timePixmap)
self.timeText.setToolTip(self.tr("Session Time")) self.timeText.setToolTip(self.tr("Session Time"))
self.timeText.setMinimumWidth(self.mainTheme.getTextWidth("00:00:00:")) self.timeText.setMinimumWidth(self.mainTheme.getTextWidth("00:00:00:"))
self.timeIcon.setContentsMargins(0, 0, 0, 0) self.timeIcon.setContentsMargins(0, 0, 0, 0)
@@ -117,6 +111,7 @@ class GuiMainStatus(QStatusBar):
logger.debug("GuiMainStatus initialisation complete") logger.debug("GuiMainStatus initialisation complete")
self.updateTheme()
self.clearStatus() self.clearStatus()
return return
@@ -132,6 +127,21 @@ class GuiMainStatus(QStatusBar):
self.updateTime() self.updateTime()
return True return True
def updateTheme(self):
"""Update theme elements.
"""
iPx = self.mainTheme.baseIconSize
self.langIcon.setPixmap(self.mainTheme.getPixmap("status_lang", (iPx, iPx)))
self.statsIcon.setPixmap(self.mainTheme.getPixmap("status_stats", (iPx, iPx)))
self.timePixmap = self.mainTheme.getPixmap("status_time", (iPx, iPx))
self.idlePixmap = self.mainTheme.getPixmap("status_idle", (iPx, iPx))
self.timeIcon.setPixmap(self.timePixmap)
return
## ##
# Setters # Setters
## ##
+123 -177
View File
@@ -38,7 +38,7 @@ from PyQt5.QtGui import (
from novelwriter.enum import nwItemLayout, nwItemType from novelwriter.enum import nwItemLayout, nwItemType
from novelwriter.error import logException from novelwriter.error import logException
from novelwriter.common import NWConfigParser, minmax, readTextFile from novelwriter.common import NWConfigParser, minmax
from novelwriter.constants import nwLabels from novelwriter.constants import nwLabels
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -67,6 +67,7 @@ class GuiTheme:
self.themeUrl = "" self.themeUrl = ""
self.themeLicense = "" self.themeLicense = ""
self.themeLicenseUrl = "" self.themeLicenseUrl = ""
self.themeIcons = ""
# GUI # GUI
self.statNone = [120, 120, 120] self.statNone = [120, 120, 120]
@@ -104,16 +105,14 @@ class GuiTheme:
self.colRepTag = [0, 0, 0] self.colRepTag = [0, 0, 0]
self.colMod = [0, 0, 0] self.colMod = [0, 0, 0]
# Changeable Settings
self.guiTheme = None
self.guiSyntax = None
self.syntaxFile = None
self.cssFile = None
self.guiFontDB = QFontDatabase()
# Class Setup # Class Setup
# =========== # ===========
# Init GUI Font
self.guiFontDB = QFontDatabase()
self._setGuiFont()
# Load Themes
self._guiPalette = QPalette() self._guiPalette = QPalette()
self._themeList = [] self._themeList = []
self._syntaxList = [] self._syntaxList = []
@@ -127,9 +126,8 @@ class GuiTheme:
self._listConf(self._availSyntax, os.path.join(self.mainConf.dataPath, "syntax")) self._listConf(self._availSyntax, os.path.join(self.mainConf.dataPath, "syntax"))
self._listConf(self._availThemes, os.path.join(self.mainConf.dataPath, "themes")) self._listConf(self._availThemes, os.path.join(self.mainConf.dataPath, "themes"))
self.updateFont() self.loadTheme()
self.updateTheme() self.loadSyntax()
self.iconCache.updateTheme()
# Icon Functions # Icon Functions
self.getIcon = self.iconCache.getIcon self.getIcon = self.iconCache.getIcon
@@ -183,77 +181,26 @@ class GuiTheme:
return int(ceil(qMetrics.boundingRect(theText).width())) return int(ceil(qMetrics.boundingRect(theText).width()))
## ##
# Actions # Theme Methods
## ##
def updateFont(self):
"""Update the GUI's font style from settings.
"""
theFont = QFont()
if self.mainConf.guiFont not in self.guiFontDB.families():
if self.mainConf.osWindows and "Arial" in self.guiFontDB.families():
# On Windows we default to Arial if possible
theFont.setFamily("Arial")
theFont.setPointSize(10)
else:
theFont = self.guiFontDB.systemFont(QFontDatabase.GeneralFont)
self.mainConf.guiFont = theFont.family()
self.mainConf.guiFontSize = theFont.pointSize()
else:
theFont.setFamily(self.mainConf.guiFont)
theFont.setPointSize(self.mainConf.guiFontSize)
qApp.setFont(theFont)
return
def updateTheme(self):
"""Update the GUI theme from theme files.
"""
self.guiTheme = self.mainConf.guiTheme
self.guiSyntax = self.mainConf.guiSyntax
self.themeFile = self._availThemes.get(self.guiTheme, None)
if self.themeFile is None:
logger.error("Could not find GUI theme '%s'", self.guiTheme)
else:
self.cssFile = self.themeFile[:-5]+".css"
self.loadTheme()
self.syntaxFile = self._availSyntax.get(self.guiSyntax, None)
if self.syntaxFile is None:
logger.error("Could not find syntax theme '%s'", self.guiSyntax)
else:
self.loadSyntax()
# Update dependant colours
backCol = qApp.palette().window().color()
textCol = qApp.palette().windowText().color()
backLCol = backCol.lightnessF()
textLCol = textCol.lightnessF()
if backLCol > textLCol:
helpLCol = textLCol + 0.65*(backLCol - textLCol)
else:
helpLCol = backLCol + 0.65*(textLCol - backLCol)
self.helpText = [int(255*helpLCol)]*3
return True
def loadTheme(self): def loadTheme(self):
"""Load the currently specified GUI theme. """Load the currently specified GUI theme.
""" """
logger.info("Loading GUI theme '%s'", self.guiTheme) guiTheme = self.mainConf.guiTheme
themeFile = self._availThemes.get(guiTheme, None)
if themeFile is None:
logger.error("Could not find GUI theme '%s'", guiTheme)
return False
# Config File # Config File
logger.info("Loading GUI theme '%s'", guiTheme)
confParser = NWConfigParser() confParser = NWConfigParser()
try: try:
with open(self.themeFile, mode="r", encoding="utf-8") as inFile: with open(themeFile, mode="r", encoding="utf-8") as inFile:
confParser.read_file(inFile) confParser.read_file(inFile)
except Exception: except Exception:
logger.error("Could not load theme settings from: %s", self.themeFile) logger.error("Could not load theme settings from: %s", themeFile)
logException() logException()
return False return False
@@ -267,6 +214,7 @@ class GuiTheme:
self.themeUrl = confParser.rdStr(cnfSec, "url", "") self.themeUrl = confParser.rdStr(cnfSec, "url", "")
self.themeLicense = confParser.rdStr(cnfSec, "license", "N/A") self.themeLicense = confParser.rdStr(cnfSec, "license", "N/A")
self.themeLicenseUrl = confParser.rdStr(cnfSec, "licenseurl", "") self.themeLicenseUrl = confParser.rdStr(cnfSec, "licenseurl", "")
self.themeIcons = confParser.rdStr(cnfSec, "icontheme", "")
# Palette # Palette
cnfSec = "Palette" cnfSec = "Palette"
@@ -285,18 +233,32 @@ class GuiTheme:
self._setPalette(confParser, cnfSec, "highlightedtext", QPalette.HighlightedText) self._setPalette(confParser, cnfSec, "highlightedtext", QPalette.HighlightedText)
self._setPalette(confParser, cnfSec, "link", QPalette.Link) self._setPalette(confParser, cnfSec, "link", QPalette.Link)
self._setPalette(confParser, cnfSec, "linkvisited", QPalette.LinkVisited) self._setPalette(confParser, cnfSec, "linkvisited", QPalette.LinkVisited)
else:
self._guiPalette = qApp.style().standardPalette()
# GUI # GUI
cnfSec = "GUI" cnfSec = "GUI"
if confParser.has_section(cnfSec): if confParser.has_section(cnfSec):
self.statNone = self._loadColour(confParser, cnfSec, "statusnone") self.statNone = self._parseColour(confParser, cnfSec, "statusnone")
self.statUnsaved = self._loadColour(confParser, cnfSec, "statusunsaved") self.statUnsaved = self._parseColour(confParser, cnfSec, "statusunsaved")
self.statSaved = self._loadColour(confParser, cnfSec, "statussaved") self.statSaved = self._parseColour(confParser, cnfSec, "statussaved")
# CSS File # Icons
cssData = readTextFile(self.cssFile) self.iconCache.loadTheme(self.themeIcons)
if cssData:
qApp.setStyleSheet(cssData) # Update Dependant Colours
backCol = self._guiPalette.window().color()
textCol = self._guiPalette.windowText().color()
backLCol = backCol.lightnessF()
textLCol = textCol.lightnessF()
if backLCol > textLCol:
helpLCol = textLCol + 0.65*(backLCol - textLCol)
else:
helpLCol = backLCol + 0.65*(textLCol - backLCol)
self.helpText = [int(255*helpLCol)]*3
# Apply Styles # Apply Styles
qApp.setPalette(self._guiPalette) qApp.setPalette(self._guiPalette)
@@ -306,14 +268,20 @@ class GuiTheme:
def loadSyntax(self): def loadSyntax(self):
"""Load the currently specified syntax highlighter theme. """Load the currently specified syntax highlighter theme.
""" """
logger.info("Loading syntax theme '%s'", self.guiSyntax) guiSyntax = self.mainConf.guiSyntax
syntaxFile = self._availSyntax.get(guiSyntax, None)
if syntaxFile is None:
logger.error("Could not find syntax theme '%s'", guiSyntax)
return False
logger.info("Loading syntax theme '%s'", guiSyntax)
confParser = NWConfigParser() confParser = NWConfigParser()
try: try:
with open(self.syntaxFile, mode="r", encoding="utf-8") as inFile: with open(syntaxFile, mode="r", encoding="utf-8") as inFile:
confParser.read_file(inFile) confParser.read_file(inFile)
except Exception: except Exception:
logger.error("Could not load syntax colours from: %s", self.syntaxFile) logger.error("Could not load syntax colours from: %s", syntaxFile)
logException() logException()
return False return False
@@ -321,32 +289,32 @@ class GuiTheme:
cnfSec = "Main" cnfSec = "Main"
if confParser.has_section(cnfSec): if confParser.has_section(cnfSec):
self.syntaxName = confParser.rdStr(cnfSec, "name", "") self.syntaxName = confParser.rdStr(cnfSec, "name", "")
self.syntaxDescription = confParser.rdStr(cnfSec, "description", "") self.syntaxDescription = confParser.rdStr(cnfSec, "description", "N/A")
self.syntaxAuthor = confParser.rdStr(cnfSec, "author", "") self.syntaxAuthor = confParser.rdStr(cnfSec, "author", "N/A")
self.syntaxCredit = confParser.rdStr(cnfSec, "credit", "") self.syntaxCredit = confParser.rdStr(cnfSec, "credit", "N/A")
self.syntaxUrl = confParser.rdStr(cnfSec, "url", "") self.syntaxUrl = confParser.rdStr(cnfSec, "url", "")
self.syntaxLicense = confParser.rdStr(cnfSec, "license", "") self.syntaxLicense = confParser.rdStr(cnfSec, "license", "N/A")
self.syntaxLicenseUrl = confParser.rdStr(cnfSec, "licenseurl", "") self.syntaxLicenseUrl = confParser.rdStr(cnfSec, "licenseurl", "")
# Syntax # Syntax
cnfSec = "Syntax" cnfSec = "Syntax"
if confParser.has_section(cnfSec): if confParser.has_section(cnfSec):
self.colBack = self._loadColour(confParser, cnfSec, "background") self.colBack = self._parseColour(confParser, cnfSec, "background")
self.colText = self._loadColour(confParser, cnfSec, "text") self.colText = self._parseColour(confParser, cnfSec, "text")
self.colLink = self._loadColour(confParser, cnfSec, "link") self.colLink = self._parseColour(confParser, cnfSec, "link")
self.colHead = self._loadColour(confParser, cnfSec, "headertext") self.colHead = self._parseColour(confParser, cnfSec, "headertext")
self.colHeadH = self._loadColour(confParser, cnfSec, "headertag") self.colHeadH = self._parseColour(confParser, cnfSec, "headertag")
self.colEmph = self._loadColour(confParser, cnfSec, "emphasis") self.colEmph = self._parseColour(confParser, cnfSec, "emphasis")
self.colDialN = self._loadColour(confParser, cnfSec, "straightquotes") self.colDialN = self._parseColour(confParser, cnfSec, "straightquotes")
self.colDialD = self._loadColour(confParser, cnfSec, "doublequotes") self.colDialD = self._parseColour(confParser, cnfSec, "doublequotes")
self.colDialS = self._loadColour(confParser, cnfSec, "singlequotes") self.colDialS = self._parseColour(confParser, cnfSec, "singlequotes")
self.colHidden = self._loadColour(confParser, cnfSec, "hidden") self.colHidden = self._parseColour(confParser, cnfSec, "hidden")
self.colKey = self._loadColour(confParser, cnfSec, "keyword") self.colKey = self._parseColour(confParser, cnfSec, "keyword")
self.colVal = self._loadColour(confParser, cnfSec, "value") self.colVal = self._parseColour(confParser, cnfSec, "value")
self.colSpell = self._loadColour(confParser, cnfSec, "spellcheckline") self.colSpell = self._parseColour(confParser, cnfSec, "spellcheckline")
self.colError = self._loadColour(confParser, cnfSec, "errorline") self.colError = self._parseColour(confParser, cnfSec, "errorline")
self.colRepTag = self._loadColour(confParser, cnfSec, "replacetag") self.colRepTag = self._parseColour(confParser, cnfSec, "replacetag")
self.colMod = self._loadColour(confParser, cnfSec, "modifier") self.colMod = self._parseColour(confParser, cnfSec, "modifier")
return True return True
@@ -388,52 +356,64 @@ class GuiTheme:
# Internal Functions # Internal Functions
## ##
def _setGuiFont(self):
"""Update the GUI's font style from settings.
"""
theFont = QFont()
if self.mainConf.guiFont not in self.guiFontDB.families():
if self.mainConf.osWindows and "Arial" in self.guiFontDB.families():
# On Windows we default to Arial if possible
theFont.setFamily("Arial")
theFont.setPointSize(10)
else:
theFont = self.guiFontDB.systemFont(QFontDatabase.GeneralFont)
self.mainConf.guiFont = theFont.family()
self.mainConf.guiFontSize = theFont.pointSize()
else:
theFont.setFamily(self.mainConf.guiFont)
theFont.setPointSize(self.mainConf.guiFontSize)
qApp.setFont(theFont)
return
def _listConf(self, targetDict, checkDir): def _listConf(self, targetDict, checkDir):
"""Scan for syntax and gui themes and populate the dictionary. """Scan for theme config files and populate the dictionary.
""" """
if not os.path.isdir(checkDir): if not os.path.isdir(checkDir):
return return False
for checkFile in os.listdir(checkDir): for checkFile in os.listdir(checkDir):
confPath = os.path.join(checkDir, checkFile) confPath = os.path.join(checkDir, checkFile)
if os.path.isfile(confPath) and confPath.endswith(".conf"): if os.path.isfile(confPath) and confPath.endswith(".conf"):
targetDict[checkFile[:-5]] = confPath targetDict[checkFile[:-5]] = confPath
return return True
def _loadColour(self, confParser, cnfSec, cnfName): def _parseColour(self, confParser, cnfSec, cnfName):
"""Load a colour value from a config string. """Parse a colour value from a config string.
""" """
if confParser.has_option(cnfSec, cnfName): if confParser.has_option(cnfSec, cnfName):
inData = confParser.get(cnfSec, cnfName).split(",") values = confParser.get(cnfSec, cnfName).split(",")
outData = [] result = []
try: try:
outData.append(int(inData[0])) result.append(minmax(int(values[0]), 0, 255))
outData.append(int(inData[1])) result.append(minmax(int(values[1]), 0, 255))
outData.append(int(inData[2])) result.append(minmax(int(values[2]), 0, 255))
except Exception: except Exception:
logger.error("Could not load theme colours for '%s' from config file", cnfName) logger.error("Could not load theme colours for '%s' from config file", cnfName)
outData = [0, 0, 0] result = [0, 0, 0]
else: else:
logger.warning("Could not find theme colours for '%s' in config file", cnfName) logger.warning("Could not find theme colours for '%s' in config file", cnfName)
outData = [0, 0, 0] result = [0, 0, 0]
return outData return result
def _setPalette(self, confParser, cnfSec, cnfName, paletteVal): def _setPalette(self, confParser, cnfSec, cnfName, paletteVal):
"""Set a palette colour value from a config string. """Set a palette colour value from a config string.
""" """
readCol = [] self._guiPalette.setColor(
if confParser.has_option(cnfSec, cnfName): paletteVal, QColor(*self._parseColour(confParser, cnfSec, cnfName))
inData = confParser.get(cnfSec, cnfName).split(",") )
try:
readCol.append(int(inData[0]))
readCol.append(int(inData[1]))
readCol.append(int(inData[2]))
except Exception:
logger.error("Could not load theme colours for '%s' from config file", cnfName)
return
if len(readCol) == 3:
self._guiPalette.setColor(paletteVal, QColor(*readCol))
return return
# End Class GuiTheme # End Class GuiTheme
@@ -492,13 +472,11 @@ class GuiIcons:
# Storage # Storage
self._qIcons = {} self._qIcons = {}
self._themeMap = {} self._themeMap = {}
self._themeList = []
self._headerDec = [] self._headerDec = []
self._confName = "icons.conf" self._confName = "icons.conf"
# Icon Theme Path # Icon Theme Path
self._iconPath = os.path.join(self.mainConf.assetPath, "icons") self._iconPath = os.path.join(self.mainConf.assetPath, "icons")
self._themePath = os.path.join(self._iconPath, "system")
# Icon Theme Meta # Icon Theme Meta
self.themeName = "" self.themeName = ""
@@ -515,20 +493,19 @@ class GuiIcons:
# Actions # Actions
## ##
def updateTheme(self): def loadTheme(self, iconTheme):
"""Update the theme map. This is more of an init, since many of """Update the theme map. This is more of an init, since many of
the GUI icons cannot really be replaced without writing specific the GUI icons cannot really be replaced without writing specific
update functions for the classes where they're used. update functions for the classes where they're used.
""" """
self._themeMap = {} self._themeMap = {}
themePath = self._getThemePath() themePath = os.path.join(self.mainConf.assetPath, "icons", iconTheme)
if themePath is None: if not os.path.isdir(themePath):
logger.warning("No icons loaded") logger.warning("No icons loaded for '%s'", iconTheme)
return False return False
self._themePath = themePath
themeConf = os.path.join(themePath, self._confName) themeConf = os.path.join(themePath, self._confName)
logger.info("Loading icon theme '%s'", self.mainConf.guiIcons) logger.info("Loading icon theme '%s'", iconTheme)
# Config File # Config File
confParser = NWConfigParser() confParser = NWConfigParser()
@@ -558,7 +535,7 @@ class GuiIcons:
if iconName not in self.ICON_KEYS: if iconName not in self.ICON_KEYS:
logger.error("Unknown icon name '%s' in config file", iconName) logger.error("Unknown icon name '%s' in config file", iconName)
else: else:
iconPath = os.path.join(self._themePath, iconFile) iconPath = os.path.join(themePath, iconFile)
if os.path.isfile(iconPath): if os.path.isfile(iconPath):
self._themeMap[iconName] = iconPath self._themeMap[iconName] = iconPath
logger.debug("Icon slot '%s' using file '%s'", iconName, iconFile) logger.debug("Icon slot '%s' using file '%s'", iconName, iconFile)
@@ -574,6 +551,14 @@ class GuiIcons:
if iconKey not in self._themeMap: if iconKey not in self._themeMap:
logger.error("No icon file specified for '%s'", iconKey) logger.error("No icon file specified for '%s'", iconKey)
# Refresh icons
for iconKey in self._qIcons:
logger.debug("Reloading icon: '%s'", iconKey)
qIcon = self._loadIcon(iconKey)
self._qIcons[iconKey] = qIcon
self._headerDec = []
return True return True
## ##
@@ -595,7 +580,7 @@ class GuiIcons:
return QPixmap() return QPixmap()
if not os.path.isfile(imgPath): if not os.path.isfile(imgPath):
logger.error("Asset '%s' not found", self.IMAGE_MAP[decoKey]) logger.error("Asset not found: %s", imgPath)
return QPixmap() return QPixmap()
theDeco = QPixmap(imgPath) theDeco = QPixmap(imgPath)
@@ -608,7 +593,7 @@ class GuiIcons:
return theDeco return theDeco
def getIcon(self, iconKey, iconSize=None): def getIcon(self, iconKey):
"""Return an icon from the icon buffer. If it doesn't exist, """Return an icon from the icon buffer. If it doesn't exist,
return, load it, and if it still doesn't exist, return an empty return, load it, and if it still doesn't exist, return an empty
icon. icon.
@@ -668,49 +653,10 @@ class GuiIcons:
] ]
return self._headerDec[minmax(hLevel, 0, 4)] return self._headerDec[minmax(hLevel, 0, 4)]
def listThemes(self):
"""Scan the icons themes folder and list all themes.
"""
if self._themeList:
return self._themeList
confParser = NWConfigParser()
for themeDir in os.listdir(self._iconPath):
themePath = os.path.join(self._iconPath, themeDir)
if not os.path.isdir(themePath):
continue
logger.debug("Checking icon theme config for '%s'", themeDir)
themeConf = os.path.join(themePath, self._confName)
themeName = _loadInternalName(confParser, themeConf)
if themeName:
self._themeList.append((themeDir, themeName))
self._themeList = sorted(self._themeList, key=lambda x: x[1])
return self._themeList
## ##
# Internal Functions # Internal Functions
## ##
def _getThemePath(self):
"""Get a valid theme path. Returns None if it fails.
"""
themePath = os.path.join(self.mainConf.assetPath, "icons", self.mainConf.guiIcons)
if not os.path.isdir(themePath):
logger.warning(
"Icon theme '%s' not found, resetting to default", self.mainConf.guiIcons
)
self.mainConf.setDefaultIconTheme()
themePath = os.path.join(self.mainConf.assetPath, "icons", self.mainConf.guiIcons)
if not os.path.isdir(themePath):
logger.error("Default icon theme not found")
return None
return themePath
def _loadIcon(self, iconKey): def _loadIcon(self, iconKey):
"""Load an icon from the assets themes folder. Is guaranteed to """Load an icon from the assets themes folder. Is guaranteed to
return a QIcon. return a QIcon.
+17 -8
View File
@@ -61,7 +61,6 @@ class GuiViewsBar(QToolBar):
self.setIconSize(QSize(iPx, iPx)) self.setIconSize(QSize(iPx, iPx))
self.setMaximumWidth(mPx) self.setMaximumWidth(mPx)
self.setContentsMargins(0, 0, 0, 0) self.setContentsMargins(0, 0, 0, 0)
self.setStyleSheet("QToolBar {border: 0px;}")
stretch = QWidget(self) stretch = QWidget(self)
stretch.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) stretch.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
@@ -70,37 +69,31 @@ class GuiViewsBar(QToolBar):
self.aProject = QAction(self.tr("Project"), self) self.aProject = QAction(self.tr("Project"), self)
self.aProject.setFont(lblFont) self.aProject.setFont(lblFont)
self.aProject.setToolTip(self.tr("Project Tree View")) self.aProject.setToolTip(self.tr("Project Tree View"))
self.aProject.setIcon(self.mainTheme.getIcon("view_editor"))
self.aProject.triggered.connect(lambda: self.viewChangeRequested.emit(nwView.PROJECT)) self.aProject.triggered.connect(lambda: self.viewChangeRequested.emit(nwView.PROJECT))
self.aNovel = QAction(self.tr("Novel"), self) self.aNovel = QAction(self.tr("Novel"), self)
self.aNovel.setFont(lblFont) self.aNovel.setFont(lblFont)
self.aNovel.setToolTip(self.tr("Novel Tree View")) self.aNovel.setToolTip(self.tr("Novel Tree View"))
self.aNovel.setIcon(self.mainTheme.getIcon("view_novel"))
self.aNovel.triggered.connect(lambda: self.viewChangeRequested.emit(nwView.NOVEL)) self.aNovel.triggered.connect(lambda: self.viewChangeRequested.emit(nwView.NOVEL))
self.aOutline = QAction(self.tr("Outline"), self) self.aOutline = QAction(self.tr("Outline"), self)
self.aOutline.setFont(lblFont) self.aOutline.setFont(lblFont)
self.aOutline.setToolTip(self.tr("Novel Outline View")) self.aOutline.setToolTip(self.tr("Novel Outline View"))
self.aOutline.setIcon(self.mainTheme.getIcon("view_outline"))
self.aOutline.triggered.connect(lambda: self.viewChangeRequested.emit(nwView.OUTLINE)) self.aOutline.triggered.connect(lambda: self.viewChangeRequested.emit(nwView.OUTLINE))
self.aBuild = QAction(self.tr("Build"), self) self.aBuild = QAction(self.tr("Build"), self)
self.aBuild.setFont(lblFont) self.aBuild.setFont(lblFont)
self.aBuild.setToolTip(self.tr("Build Novel Project")) self.aBuild.setToolTip(self.tr("Build Novel Project"))
self.aBuild.setIcon(self.mainTheme.getIcon("view_build"))
self.aBuild.triggered.connect(lambda: self.mainGui.showBuildProjectDialog()) self.aBuild.triggered.connect(lambda: self.mainGui.showBuildProjectDialog())
self.aDetails = QAction(self.tr("Details"), self) self.aDetails = QAction(self.tr("Details"), self)
self.aDetails.setFont(lblFont) self.aDetails.setFont(lblFont)
self.aDetails.setToolTip(self.tr("Project Details")) self.aDetails.setToolTip(self.tr("Project Details"))
self.aDetails.setIcon(self.mainTheme.getIcon("proj_details"))
self.aDetails.triggered.connect(lambda: self.mainGui.showProjectDetailsDialog()) self.aDetails.triggered.connect(lambda: self.mainGui.showProjectDetailsDialog())
self.aStats = QAction(self.tr("Stats"), self) self.aStats = QAction(self.tr("Stats"), self)
self.aStats.setFont(lblFont) self.aStats.setFont(lblFont)
self.aStats.setToolTip(self.tr("Writing Statistics")) self.aStats.setToolTip(self.tr("Writing Statistics"))
self.aStats.setIcon(self.mainTheme.getIcon("proj_stats"))
self.aStats.triggered.connect(lambda: self.mainGui.showWritingStatsDialog()) self.aStats.triggered.connect(lambda: self.mainGui.showWritingStatsDialog())
# Settings Menu # Settings Menu
@@ -114,7 +107,6 @@ class GuiViewsBar(QToolBar):
self.tbSettings = QToolButton(self) self.tbSettings = QToolButton(self)
self.tbSettings.setFont(lblFont) self.tbSettings.setFont(lblFont)
self.tbSettings.setText(self.tr("Settings")) self.tbSettings.setText(self.tr("Settings"))
self.tbSettings.setIcon(self.mainTheme.getIcon("settings"))
self.tbSettings.setMenu(self.mSettings) self.tbSettings.setMenu(self.mSettings)
self.tbSettings.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) self.tbSettings.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
self.tbSettings.setPopupMode(QToolButton.InstantPopup) self.tbSettings.setPopupMode(QToolButton.InstantPopup)
@@ -129,8 +121,25 @@ class GuiViewsBar(QToolBar):
self.addAction(self.aStats) self.addAction(self.aStats)
self.addWidget(self.tbSettings) self.addWidget(self.tbSettings)
self.updateTheme()
logger.debug("GuiViewsBar initialisation complete") logger.debug("GuiViewsBar initialisation complete")
return return
def updateTheme(self):
"""Initialise GUI elements that depend on specific settings.
"""
self.setStyleSheet("QToolBar {border: 0px;}")
self.aProject.setIcon(self.mainTheme.getIcon("view_editor"))
self.aNovel.setIcon(self.mainTheme.getIcon("view_novel"))
self.aOutline.setIcon(self.mainTheme.getIcon("view_outline"))
self.aBuild.setIcon(self.mainTheme.getIcon("view_build"))
self.aDetails.setIcon(self.mainTheme.getIcon("proj_details"))
self.aStats.setIcon(self.mainTheme.getIcon("proj_stats"))
self.tbSettings.setIcon(self.mainTheme.getIcon("settings"))
return
# END Class GuiViewsBar # END Class GuiViewsBar
+27 -1
View File
@@ -907,13 +907,39 @@ class GuiMain(QMainWindow):
if dlgConf.result() == QDialog.Accepted: if dlgConf.result() == QDialog.Accepted:
logger.debug("Applying new preferences") logger.debug("Applying new preferences")
self.initMain() self.initMain()
self.mainTheme.updateTheme()
self.saveDocument() self.saveDocument()
if dlgConf.needsRestart:
self.makeAlert(self.tr(
"Some changes will not be applied until novelWriter has been restarted."
), nwAlert.INFO)
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
self.mainTheme.loadTheme()
self.docEditor.updateTheme()
self.docViewer.updateTheme()
self.viewsBar.updateTheme()
self.projView.updateTheme()
self.novelView.updateTheme()
self.outlineView.updateTheme()
self.itemDetails.updateTheme()
self.mainStatus.updateTheme()
if dlgConf.updateSyntax:
self.mainTheme.loadSyntax()
self.docEditor.updateSyntaxColours()
self.docEditor.initEditor() self.docEditor.initEditor()
self.docViewer.initViewer() self.docViewer.initViewer()
self.projView.initSettings() self.projView.initSettings()
self.novelView.initSettings() self.novelView.initSettings()
self.outlineView.initSettings() self.outlineView.initSettings()
self._updateStatusWordCount() self._updateStatusWordCount()
return return
+5 -1
View File
@@ -162,7 +162,11 @@ def mockGUI(monkeypatch, tmpConf):
def nwGUI(qtbot, monkeypatch, fncDir, fncConf): def nwGUI(qtbot, monkeypatch, fncDir, fncConf):
"""Create an instance of the novelWriter GUI. """Create an instance of the novelWriter GUI.
""" """
monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Ok)
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Ok)
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Ok)
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr("novelwriter.CONFIG", fncConf) monkeypatch.setattr("novelwriter.CONFIG", fncConf)
nwGUI = novelwriter.main(["--testmode", "--config=%s" % fncDir, "--data=%s" % fncDir]) nwGUI = novelwriter.main(["--testmode", "--config=%s" % fncDir, "--data=%s" % fncDir])
qtbot.addWidget(nwGUI) qtbot.addWidget(nwGUI)
+1 -2
View File
@@ -1,8 +1,7 @@
[Main] [Main]
timestamp = 2021-12-31 16:45:32 timestamp = 2022-10-26 11:19:49
theme = default theme = default
syntax = default_light syntax = default_light
icons = typicons_light
guifont = guifont =
guifontsize = 11 guifontsize = 11
lastnotes = 0x0 lastnotes = 0x0
@@ -1,9 +1,8 @@
[Main] [Main]
timestamp = 2021-12-31 16:45:34 timestamp = 2022-10-26 11:19:51
theme = default theme = default
syntax = default_light syntax = default_light
icons = typicons_light guifont = Cantarell
guifont = Sans
guifontsize = 12 guifontsize = 12
lastnotes = 0x0 lastnotes = 0x0
guilang = en_GB guilang = en_GB
@@ -12,7 +11,7 @@ hidehscroll = True
[Sizes] [Sizes]
geometry = 1200, 650 geometry = 1200, 650
preferences = 670, 589 preferences = 699, 614
projcols = 200, 60, 140 projcols = 200, 60, 140
mainpane = 300, 800 mainpane = 300, 800
docpane = 400, 400 docpane = 400, 400
-26
View File
@@ -192,32 +192,6 @@ def testBaseConfig_Init(monkeypatch, tmpDir, fncDir, outDir, refDir, filesDir):
tstConf.doReplaceSQuote = orDoSng tstConf.doReplaceSQuote = orDoSng
assert tstConf.saveConfig() is True assert tstConf.saveConfig() is True
# Test Correcting icon theme
origIcons = tstConf.guiIcons
tstConf.guiIcons = "typicons_colour_dark"
assert tstConf.saveConfig() is True
assert tstConf.loadConfig() is True
assert tstConf.guiIcons == "typicons_dark"
tstConf.guiIcons = "typicons_grey_dark"
assert tstConf.saveConfig() is True
assert tstConf.loadConfig() is True
assert tstConf.guiIcons == "typicons_dark"
tstConf.guiIcons = "typicons_colour_light"
assert tstConf.saveConfig() is True
assert tstConf.loadConfig() is True
assert tstConf.guiIcons == "typicons_light"
tstConf.guiIcons = "typicons_grey_light"
assert tstConf.saveConfig() is True
assert tstConf.loadConfig() is True
assert tstConf.guiIcons == "typicons_light"
tstConf.guiIcons = origIcons
assert tstConf.saveConfig()
# Localisation # Localisation
# ============ # ============
+1 -6
View File
@@ -29,12 +29,9 @@ from novelwriter.dialogs.about import GuiAbout
@pytest.mark.gui @pytest.mark.gui
def testDlgAbout_NWDialog(qtbot, monkeypatch, nwGUI): def testDlgAbout_NWDialog(qtbot, nwGUI):
"""Test the novelWriter about dialogs. """Test the novelWriter about dialogs.
""" """
# Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
# NW About # NW About
nwGUI.mainTheme.themeName = "A Theme" nwGUI.mainTheme.themeName = "A Theme"
nwGUI.mainTheme.themeAuthor = "An Author" nwGUI.mainTheme.themeAuthor = "An Author"
@@ -74,8 +71,6 @@ def testDlgAbout_NWDialog(qtbot, monkeypatch, nwGUI):
def testDlgAbout_QtDialog(monkeypatch, nwGUI): def testDlgAbout_QtDialog(monkeypatch, nwGUI):
"""Test the Qt about dialogs. """Test the Qt about dialogs.
""" """
# Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "aboutQt", lambda *a, **k: None) monkeypatch.setattr(QMessageBox, "aboutQt", lambda *a, **k: None)
# Open About # Open About
+4 -10
View File
@@ -22,7 +22,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
import pytest import pytest
from PyQt5.QtCore import QItemSelectionModel from PyQt5.QtCore import QItemSelectionModel
from PyQt5.QtWidgets import QAction, QListWidgetItem, QDialog, QMessageBox from PyQt5.QtWidgets import QAction, QListWidgetItem, QDialog
from novelwriter.dialogs.quotes import GuiQuoteSelect from novelwriter.dialogs.quotes import GuiQuoteSelect
from novelwriter.dialogs.updates import GuiUpdates from novelwriter.dialogs.updates import GuiUpdates
@@ -30,12 +30,9 @@ from novelwriter.dialogs.editlabel import GuiEditLabel
@pytest.mark.gui @pytest.mark.gui
def testDlgOther_QuoteSelect(qtbot, monkeypatch, nwGUI): def testDlgOther_QuoteSelect(qtbot, nwGUI):
"""Test the quote symbols dialog. """Test the quote symbols dialog.
""" """
# Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
nwQuot = GuiQuoteSelect(nwGUI) nwQuot = GuiQuoteSelect(nwGUI)
nwQuot.show() nwQuot.show()
@@ -52,7 +49,7 @@ def testDlgOther_QuoteSelect(qtbot, monkeypatch, nwGUI):
assert nwQuot.result() == QDialog.Accepted assert nwQuot.result() == QDialog.Accepted
assert nwQuot.selectedQuote == lastItem assert nwQuot.selectedQuote == lastItem
# qtbot.stopForInteraction() # qtbot.stop()
nwQuot._doReject() nwQuot._doReject()
nwQuot.close() nwQuot.close()
@@ -63,9 +60,6 @@ def testDlgOther_QuoteSelect(qtbot, monkeypatch, nwGUI):
def testDlgOther_Updates(qtbot, monkeypatch, nwGUI): def testDlgOther_Updates(qtbot, monkeypatch, nwGUI):
"""Test the check for updates dialog. """Test the check for updates dialog.
""" """
# Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
nwUpdate = GuiUpdates(nwGUI) nwUpdate = GuiUpdates(nwGUI)
nwUpdate.show() nwUpdate.show()
@@ -95,7 +89,7 @@ def testDlgOther_Updates(qtbot, monkeypatch, nwGUI):
# Trigger from Menu # Trigger from Menu
nwGUI.mainMenu.aUpdates.activate(QAction.Trigger) nwGUI.mainMenu.aUpdates.activate(QAction.Trigger)
# qtbot.stopForInteraction() # qtbot.stop()
nwUpdate._doClose() nwUpdate._doClose()
# END Test testDlgOther_Updates # END Test testDlgOther_Updates
+1 -6
View File
@@ -24,19 +24,14 @@ import pytest
from tools import buildTestProject, C from tools import buildTestProject, C
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QMessageBox
from novelwriter.dialogs.docmerge import GuiDocMerge from novelwriter.dialogs.docmerge import GuiDocMerge
@pytest.mark.gui @pytest.mark.gui
def testDlgMerge_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd): def testDlgMerge_Main(qtbot, nwGUI, fncProj, mockRnd):
"""Test the merge documents tool. """Test the merge documents tool.
""" """
# Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Ok)
# Create a new project # Create a new project
buildTestProject(nwGUI, fncProj) buildTestProject(nwGUI, fncProj)
-5
View File
@@ -23,8 +23,6 @@ import pytest
from tools import C, buildTestProject from tools import C, buildTestProject
from PyQt5.QtWidgets import QMessageBox
from novelwriter.dialogs.docsplit import GuiDocSplit from novelwriter.dialogs.docsplit import GuiDocSplit
from novelwriter.dialogs.editlabel import GuiEditLabel from novelwriter.dialogs.editlabel import GuiEditLabel
@@ -33,9 +31,6 @@ from novelwriter.dialogs.editlabel import GuiEditLabel
def testDlgSplit_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd): def testDlgSplit_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
"""Test the split document tool. """Test the split document tool.
""" """
# Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Ok)
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True)) monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
# Create a new project # Create a new project
+43 -36
View File
@@ -35,9 +35,7 @@ from novelwriter.config import Config
from novelwriter.dialogs.quotes import GuiQuoteSelect from novelwriter.dialogs.quotes import GuiQuoteSelect
from novelwriter.dialogs.preferences import GuiPreferences from novelwriter.dialogs.preferences import GuiPreferences
keyDelay = 2 KEY_DELAY = 1
typeDelay = 1
stepDelay = 20
@pytest.mark.gui @pytest.mark.gui
@@ -63,7 +61,6 @@ def testDlgPreferences_Main(qtbot, monkeypatch, fncDir, outDir, refDir):
nwGUI = novelwriter.main(["--testmode", "--config=%s" % fncDir, "--data=%s" % fncDir]) nwGUI = novelwriter.main(["--testmode", "--config=%s" % fncDir, "--data=%s" % fncDir])
qtbot.addWidget(nwGUI) qtbot.addWidget(nwGUI)
nwGUI.show() nwGUI.show()
qtbot.wait(stepDelay)
theConf = nwGUI.mainConf theConf = nwGUI.mainConf
assert theConf.confPath == fncDir assert theConf.confPath == fncDir
@@ -72,30 +69,40 @@ def testDlgPreferences_Main(qtbot, monkeypatch, fncDir, outDir, refDir):
monkeypatch.setattr(GuiPreferences, "result", lambda *a: QDialog.Accepted) monkeypatch.setattr(GuiPreferences, "result", lambda *a: QDialog.Accepted)
monkeypatch.setattr(nwGUI.docEditor.spEnchant, "listDictionaries", lambda: [("en", "none")]) monkeypatch.setattr(nwGUI.docEditor.spEnchant, "listDictionaries", lambda: [("en", "none")])
nwGUI.mainMenu.aPreferences.activate(QAction.Trigger) with monkeypatch.context() as mp:
qtbot.waitUntil(lambda: getGuiItem("GuiPreferences") is not None, timeout=1000) 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)
nwPrefs = getGuiItem("GuiPreferences") nwPrefs = getGuiItem("GuiPreferences")
assert isinstance(nwPrefs, GuiPreferences) assert isinstance(nwPrefs, GuiPreferences)
nwPrefs.show() nwPrefs.show()
assert nwPrefs.mainConf.confPath == fncDir assert nwPrefs.mainConf.confPath == fncDir
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(keyDelay) qtbot.wait(KEY_DELAY)
tabGeneral = nwPrefs.tabGeneral tabGeneral = nwPrefs.tabGeneral
nwPrefs._tabBox.setCurrentWidget(tabGeneral) nwPrefs._tabBox.setCurrentWidget(tabGeneral)
qtbot.wait(keyDelay) qtbot.wait(KEY_DELAY)
assert tabGeneral.showFullPath.isChecked() assert tabGeneral.showFullPath.isChecked()
qtbot.mouseClick(tabGeneral.showFullPath, Qt.LeftButton) qtbot.mouseClick(tabGeneral.showFullPath, Qt.LeftButton)
assert not tabGeneral.showFullPath.isChecked() assert not tabGeneral.showFullPath.isChecked()
qtbot.wait(keyDelay) qtbot.wait(KEY_DELAY)
assert not tabGeneral.hideVScroll.isChecked() assert not tabGeneral.hideVScroll.isChecked()
qtbot.mouseClick(tabGeneral.hideVScroll, Qt.LeftButton) qtbot.mouseClick(tabGeneral.hideVScroll, Qt.LeftButton)
assert tabGeneral.hideVScroll.isChecked() assert tabGeneral.hideVScroll.isChecked()
qtbot.wait(keyDelay) qtbot.wait(KEY_DELAY)
assert not tabGeneral.hideHScroll.isChecked() assert not tabGeneral.hideHScroll.isChecked()
qtbot.mouseClick(tabGeneral.hideHScroll, Qt.LeftButton) qtbot.mouseClick(tabGeneral.hideHScroll, Qt.LeftButton)
assert tabGeneral.hideHScroll.isChecked() assert tabGeneral.hideHScroll.isChecked()
@@ -104,21 +111,21 @@ def testDlgPreferences_Main(qtbot, monkeypatch, fncDir, outDir, refDir):
monkeypatch.setattr(QFontDialog, "getFont", lambda font, obj: (font, True)) monkeypatch.setattr(QFontDialog, "getFont", lambda font, obj: (font, True))
qtbot.mouseClick(tabGeneral.fontButton, Qt.LeftButton) qtbot.mouseClick(tabGeneral.fontButton, Qt.LeftButton)
qtbot.wait(keyDelay) qtbot.wait(KEY_DELAY)
tabGeneral.guiFontSize.setValue(12) tabGeneral.guiFontSize.setValue(12)
# Projects Settings # Projects Settings
qtbot.wait(keyDelay) qtbot.wait(KEY_DELAY)
tabProjects = nwPrefs.tabProjects tabProjects = nwPrefs.tabProjects
nwPrefs._tabBox.setCurrentWidget(tabProjects) nwPrefs._tabBox.setCurrentWidget(tabProjects)
tabProjects.backupPath = "no/where" tabProjects.backupPath = "no/where"
qtbot.wait(keyDelay) qtbot.wait(KEY_DELAY)
assert not tabProjects.backupOnClose.isChecked() assert not tabProjects.backupOnClose.isChecked()
qtbot.mouseClick(tabProjects.backupOnClose, Qt.LeftButton) qtbot.mouseClick(tabProjects.backupOnClose, Qt.LeftButton)
assert tabProjects.backupOnClose.isChecked() assert tabProjects.backupOnClose.isChecked()
# qtbot.stopForInteraction() # qtbot.stop()
# Check Browse button # Check Browse button
monkeypatch.setattr(QFileDialog, "getExistingDirectory", lambda *a, **k: "") monkeypatch.setattr(QFileDialog, "getExistingDirectory", lambda *a, **k: "")
@@ -126,104 +133,104 @@ def testDlgPreferences_Main(qtbot, monkeypatch, fncDir, outDir, refDir):
monkeypatch.setattr(QFileDialog, "getExistingDirectory", lambda *a, **k: "some/dir") monkeypatch.setattr(QFileDialog, "getExistingDirectory", lambda *a, **k: "some/dir")
qtbot.mouseClick(tabProjects.backupGetPath, Qt.LeftButton) qtbot.mouseClick(tabProjects.backupGetPath, Qt.LeftButton)
qtbot.wait(keyDelay) qtbot.wait(KEY_DELAY)
tabProjects.autoSaveDoc.setValue(20) tabProjects.autoSaveDoc.setValue(20)
tabProjects.autoSaveProj.setValue(40) tabProjects.autoSaveProj.setValue(40)
# Document Settings # Document Settings
qtbot.wait(keyDelay) qtbot.wait(KEY_DELAY)
tabDocs = nwPrefs.tabDocs tabDocs = nwPrefs.tabDocs
nwPrefs._tabBox.setCurrentWidget(tabDocs) nwPrefs._tabBox.setCurrentWidget(tabDocs)
qtbot.wait(keyDelay) qtbot.wait(KEY_DELAY)
qtbot.mouseClick(tabDocs.fontButton, Qt.LeftButton) qtbot.mouseClick(tabDocs.fontButton, Qt.LeftButton)
qtbot.wait(keyDelay) qtbot.wait(KEY_DELAY)
tabDocs.textSize.setValue(13) tabDocs.textSize.setValue(13)
tabDocs.textWidth.setValue(700) tabDocs.textWidth.setValue(700)
tabDocs.focusWidth.setValue(900) tabDocs.focusWidth.setValue(900)
tabDocs.textMargin.setValue(45) tabDocs.textMargin.setValue(45)
tabDocs.tabWidth.setValue(45) tabDocs.tabWidth.setValue(45)
qtbot.wait(keyDelay) qtbot.wait(KEY_DELAY)
assert not tabDocs.hideFocusFooter.isChecked() assert not tabDocs.hideFocusFooter.isChecked()
qtbot.mouseClick(tabDocs.hideFocusFooter, Qt.LeftButton) qtbot.mouseClick(tabDocs.hideFocusFooter, Qt.LeftButton)
assert tabDocs.hideFocusFooter.isChecked() assert tabDocs.hideFocusFooter.isChecked()
qtbot.wait(keyDelay) qtbot.wait(KEY_DELAY)
assert not tabDocs.doJustify.isChecked() assert not tabDocs.doJustify.isChecked()
qtbot.mouseClick(tabDocs.doJustify, Qt.LeftButton) qtbot.mouseClick(tabDocs.doJustify, Qt.LeftButton)
assert tabDocs.doJustify.isChecked() assert tabDocs.doJustify.isChecked()
# Editor Settings # Editor Settings
qtbot.wait(keyDelay) qtbot.wait(KEY_DELAY)
tabEditor = nwPrefs.tabEditor tabEditor = nwPrefs.tabEditor
nwPrefs._tabBox.setCurrentWidget(tabEditor) nwPrefs._tabBox.setCurrentWidget(tabEditor)
qtbot.wait(keyDelay) qtbot.wait(KEY_DELAY)
assert not tabEditor.showTabsNSpaces.isChecked() assert not tabEditor.showTabsNSpaces.isChecked()
qtbot.mouseClick(tabEditor.showTabsNSpaces, Qt.LeftButton) qtbot.mouseClick(tabEditor.showTabsNSpaces, Qt.LeftButton)
assert tabEditor.showTabsNSpaces.isChecked() assert tabEditor.showTabsNSpaces.isChecked()
qtbot.wait(keyDelay) qtbot.wait(KEY_DELAY)
assert not tabEditor.showLineEndings.isChecked() assert not tabEditor.showLineEndings.isChecked()
qtbot.mouseClick(tabEditor.showLineEndings, Qt.LeftButton) qtbot.mouseClick(tabEditor.showLineEndings, Qt.LeftButton)
assert tabEditor.showLineEndings.isChecked() assert tabEditor.showLineEndings.isChecked()
qtbot.wait(keyDelay) qtbot.wait(KEY_DELAY)
assert not tabEditor.autoScroll.isChecked() assert not tabEditor.autoScroll.isChecked()
qtbot.mouseClick(tabEditor.autoScroll, Qt.LeftButton) qtbot.mouseClick(tabEditor.autoScroll, Qt.LeftButton)
assert tabEditor.autoScroll.isChecked() assert tabEditor.autoScroll.isChecked()
qtbot.wait(keyDelay) qtbot.wait(KEY_DELAY)
tabEditor.scrollPastEnd.setValue(0) tabEditor.scrollPastEnd.setValue(0)
qtbot.wait(keyDelay) qtbot.wait(KEY_DELAY)
tabEditor.bigDocLimit.setValue(500) tabEditor.bigDocLimit.setValue(500)
# Syntax Settings # Syntax Settings
qtbot.wait(keyDelay) qtbot.wait(KEY_DELAY)
tabSyntax = nwPrefs.tabSyntax tabSyntax = nwPrefs.tabSyntax
nwPrefs._tabBox.setCurrentWidget(tabSyntax) nwPrefs._tabBox.setCurrentWidget(tabSyntax)
qtbot.wait(keyDelay) qtbot.wait(KEY_DELAY)
assert tabSyntax.highlightQuotes.isChecked() assert tabSyntax.highlightQuotes.isChecked()
qtbot.mouseClick(tabSyntax.highlightQuotes, Qt.LeftButton) qtbot.mouseClick(tabSyntax.highlightQuotes, Qt.LeftButton)
assert not tabSyntax.highlightQuotes.isChecked() assert not tabSyntax.highlightQuotes.isChecked()
qtbot.wait(keyDelay) qtbot.wait(KEY_DELAY)
assert tabSyntax.highlightEmph.isChecked() assert tabSyntax.highlightEmph.isChecked()
qtbot.mouseClick(tabSyntax.highlightEmph, Qt.LeftButton) qtbot.mouseClick(tabSyntax.highlightEmph, Qt.LeftButton)
assert not tabSyntax.highlightEmph.isChecked() assert not tabSyntax.highlightEmph.isChecked()
# Automation Settings # Automation Settings
qtbot.wait(keyDelay) qtbot.wait(KEY_DELAY)
tabAuto = nwPrefs.tabAuto tabAuto = nwPrefs.tabAuto
nwPrefs._tabBox.setCurrentWidget(tabAuto) nwPrefs._tabBox.setCurrentWidget(tabAuto)
qtbot.wait(keyDelay) qtbot.wait(KEY_DELAY)
assert tabAuto.autoSelect.isChecked() assert tabAuto.autoSelect.isChecked()
qtbot.mouseClick(tabAuto.autoSelect, Qt.LeftButton) qtbot.mouseClick(tabAuto.autoSelect, Qt.LeftButton)
assert not tabAuto.autoSelect.isChecked() assert not tabAuto.autoSelect.isChecked()
qtbot.wait(keyDelay) qtbot.wait(KEY_DELAY)
assert tabAuto.doReplace.isChecked() assert tabAuto.doReplace.isChecked()
qtbot.mouseClick(tabAuto.doReplace, Qt.LeftButton) qtbot.mouseClick(tabAuto.doReplace, Qt.LeftButton)
assert not tabAuto.doReplace.isChecked() assert not tabAuto.doReplace.isChecked()
qtbot.wait(keyDelay) qtbot.wait(KEY_DELAY)
assert not tabAuto.doReplaceSQuote.isEnabled() assert not tabAuto.doReplaceSQuote.isEnabled()
assert not tabAuto.doReplaceDQuote.isEnabled() assert not tabAuto.doReplaceDQuote.isEnabled()
assert not tabAuto.doReplaceDash.isEnabled() assert not tabAuto.doReplaceDash.isEnabled()
assert not tabAuto.doReplaceDots.isEnabled() assert not tabAuto.doReplaceDots.isEnabled()
# Quotation Style # Quotation Style
qtbot.wait(keyDelay) qtbot.wait(KEY_DELAY)
tabQuote = nwPrefs.tabQuote tabQuote = nwPrefs.tabQuote
nwPrefs._tabBox.setCurrentWidget(tabQuote) nwPrefs._tabBox.setCurrentWidget(tabQuote)
monkeypatch.setattr(GuiQuoteSelect, "selectedQuote", "'") monkeypatch.setattr(GuiQuoteSelect, "selectedQuote", "'")
monkeypatch.setattr(GuiQuoteSelect, "exec_", lambda *args: QDialog.Accepted) monkeypatch.setattr(GuiQuoteSelect, "exec_", lambda *a: QDialog.Accepted)
qtbot.mouseClick(tabQuote.btnDoubleStyleC, Qt.LeftButton) qtbot.mouseClick(tabQuote.btnDoubleStyleC, Qt.LeftButton)
# Save and Check Config # Save and Check Config
@@ -249,6 +256,6 @@ def testDlgPreferences_Main(qtbot, monkeypatch, fncDir, outDir, refDir):
novelwriter.CONFIG = origConf novelwriter.CONFIG = origConf
nwGUI.closeMain() nwGUI.closeMain()
# qtbot.stopForInteraction() # qtbot.stop()
# END Test testDlgPreferences_Main # END Test testDlgPreferences_Main
+3 -14
View File
@@ -23,25 +23,15 @@ import pytest
from tools import getGuiItem from tools import getGuiItem
from PyQt5.QtWidgets import QAction, QMessageBox from PyQt5.QtWidgets import QAction
from novelwriter.dialogs.projdetails import GuiProjectDetails from novelwriter.dialogs.projdetails import GuiProjectDetails
keyDelay = 2
typeDelay = 1
stepDelay = 20
@pytest.mark.gui @pytest.mark.gui
def testDlgProjDetails_Dialog(qtbot, monkeypatch, nwGUI, nwLipsum): def testDlgProjDetails_Dialog(qtbot, nwGUI, nwLipsum):
"""Test the project details dialog. """Test the project details dialog.
""" """
# Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
# Create a project to work on # Create a project to work on
assert nwGUI.openProject(nwLipsum) assert nwGUI.openProject(nwLipsum)
assert nwGUI.rebuildIndex(beQuiet=True) assert nwGUI.rebuildIndex(beQuiet=True)
@@ -54,7 +44,6 @@ def testDlgProjDetails_Dialog(qtbot, monkeypatch, nwGUI, nwLipsum):
projDet = getGuiItem("GuiProjectDetails") projDet = getGuiItem("GuiProjectDetails")
assert isinstance(projDet, GuiProjectDetails) assert isinstance(projDet, GuiProjectDetails)
qtbot.wait(stepDelay)
# Overview Page # Overview Page
# ============= # =============
@@ -105,7 +94,7 @@ def testDlgProjDetails_Dialog(qtbot, monkeypatch, nwGUI, nwLipsum):
assert tocTree.topLevelItem(i).text(tocTab.C_PAGES) == thePages[i] 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_PAGE) == thePage[i]
# qtbot.stopForInteraction() # qtbot.stop()
# Clean Up # Clean Up
projDet._doClose() projDet._doClose()
+2 -21
View File
@@ -26,29 +26,19 @@ from tools import getGuiItem
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
QDialogButtonBox, QTreeWidgetItem, QDialog, QAction, QFileDialog, QDialogButtonBox, QTreeWidgetItem, QDialog, QAction, QFileDialog
QMessageBox
) )
from novelwriter.dialogs.projload import GuiProjectLoad from novelwriter.dialogs.projload import GuiProjectLoad
keyDelay = 2
typeDelay = 1
stepDelay = 20
@pytest.mark.gui @pytest.mark.gui
def testDlgLoadProject_Main(qtbot, monkeypatch, nwGUI, nwMinimal): def testDlgLoadProject_Main(qtbot, monkeypatch, nwGUI, nwMinimal):
"""Test the load project wizard. """Test the load project wizard.
""" """
# Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
assert nwGUI.openProject(nwMinimal) assert nwGUI.openProject(nwMinimal)
assert nwGUI.closeProject() assert nwGUI.closeProject()
qtbot.wait(stepDelay)
monkeypatch.setattr(GuiProjectLoad, "exec_", lambda *a: None) monkeypatch.setattr(GuiProjectLoad, "exec_", lambda *a: None)
monkeypatch.setattr(GuiProjectLoad, "result", lambda *a: QDialog.Accepted) monkeypatch.setattr(GuiProjectLoad, "result", lambda *a: QDialog.Accepted)
nwGUI.mainMenu.aOpenProject.activate(QAction.Trigger) nwGUI.mainMenu.aOpenProject.activate(QAction.Trigger)
@@ -58,22 +48,18 @@ def testDlgLoadProject_Main(qtbot, monkeypatch, nwGUI, nwMinimal):
assert isinstance(nwLoad, GuiProjectLoad) assert isinstance(nwLoad, GuiProjectLoad)
nwLoad.show() nwLoad.show()
qtbot.wait(stepDelay)
recentCount = nwLoad.listBox.topLevelItemCount() recentCount = nwLoad.listBox.topLevelItemCount()
assert recentCount > 0 assert recentCount > 0
qtbot.wait(stepDelay)
selItem = nwLoad.listBox.topLevelItem(0) selItem = nwLoad.listBox.topLevelItem(0)
selPath = selItem.data(nwLoad.C_NAME, Qt.UserRole) selPath = selItem.data(nwLoad.C_NAME, Qt.UserRole)
assert isinstance(selItem, QTreeWidgetItem) assert isinstance(selItem, QTreeWidgetItem)
qtbot.wait(stepDelay)
nwLoad.selPath.setText("") nwLoad.selPath.setText("")
nwLoad.listBox.setCurrentItem(selItem) nwLoad.listBox.setCurrentItem(selItem)
nwLoad._doSelectRecent() nwLoad._doSelectRecent()
assert nwLoad.selPath.text() == selPath assert nwLoad.selPath.text() == selPath
qtbot.wait(stepDelay)
qtbot.mouseClick(nwLoad.buttonBox.button(QDialogButtonBox.Open), Qt.LeftButton) qtbot.mouseClick(nwLoad.buttonBox.button(QDialogButtonBox.Open), Qt.LeftButton)
assert nwLoad.openPath == selPath assert nwLoad.openPath == selPath
assert nwLoad.openState == nwLoad.OPEN_STATE assert nwLoad.openState == nwLoad.OPEN_STATE
@@ -81,27 +67,22 @@ def testDlgLoadProject_Main(qtbot, monkeypatch, nwGUI, nwMinimal):
# Just create a new project load from scratch for the rest of the test # Just create a new project load from scratch for the rest of the test
del nwLoad del nwLoad
qtbot.wait(stepDelay)
nwGUI.mainMenu.aOpenProject.activate(QAction.Trigger) nwGUI.mainMenu.aOpenProject.activate(QAction.Trigger)
qtbot.waitUntil(lambda: getGuiItem("GuiProjectLoad") is not None, timeout=1000) qtbot.waitUntil(lambda: getGuiItem("GuiProjectLoad") is not None, timeout=1000)
qtbot.wait(stepDelay)
nwLoad = getGuiItem("GuiProjectLoad") nwLoad = getGuiItem("GuiProjectLoad")
assert isinstance(nwLoad, GuiProjectLoad) assert isinstance(nwLoad, GuiProjectLoad)
nwLoad.show() nwLoad.show()
qtbot.wait(stepDelay)
qtbot.mouseClick(nwLoad.buttonBox.button(QDialogButtonBox.Cancel), Qt.LeftButton) qtbot.mouseClick(nwLoad.buttonBox.button(QDialogButtonBox.Cancel), Qt.LeftButton)
assert nwLoad.openPath is None assert nwLoad.openPath is None
assert nwLoad.openState == nwLoad.NONE_STATE assert nwLoad.openState == nwLoad.NONE_STATE
qtbot.wait(stepDelay)
nwLoad.show() nwLoad.show()
qtbot.mouseClick(nwLoad.newButton, Qt.LeftButton) qtbot.mouseClick(nwLoad.newButton, Qt.LeftButton)
assert nwLoad.openPath is None assert nwLoad.openPath is None
assert nwLoad.openState == nwLoad.NEW_STATE assert nwLoad.openState == nwLoad.NEW_STATE
qtbot.wait(stepDelay)
nwLoad.show() nwLoad.show()
nwLoad._doDeleteRecent() nwLoad._doDeleteRecent()
assert nwLoad.listBox.topLevelItemCount() == recentCount - 1 assert nwLoad.listBox.topLevelItemCount() == recentCount - 1
@@ -113,6 +94,6 @@ def testDlgLoadProject_Main(qtbot, monkeypatch, nwGUI, nwMinimal):
assert nwLoad.openState == nwLoad.OPEN_STATE assert nwLoad.openState == nwLoad.OPEN_STATE
nwLoad.close() nwLoad.close()
# qtbot.stopForInteraction() # qtbot.stop()
# END Test testDlgLoadProject_Main # END Test testDlgLoadProject_Main
+14 -32
View File
@@ -26,14 +26,12 @@ from tools import C, getGuiItem, buildTestProject
from PyQt5.QtGui import QColor from PyQt5.QtGui import QColor
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QDialog, QAction, QMessageBox, QColorDialog from PyQt5.QtWidgets import QDialog, QAction, QColorDialog
from novelwriter.dialogs.editlabel import GuiEditLabel from novelwriter.dialogs.editlabel import GuiEditLabel
from novelwriter.dialogs.projsettings import GuiProjectSettings from novelwriter.dialogs.projsettings import GuiProjectSettings
keyDelay = 2 KEY_DELAY = 1
typeDelay = 1
stepDelay = 20
@pytest.mark.gui @pytest.mark.gui
@@ -41,10 +39,6 @@ def testDlgProjSettings_Dialog(qtbot, monkeypatch, nwGUI):
"""Test the main dialog class. Saving settings is not tested in this """Test the main dialog class. Saving settings is not tested in this
test, but are instead tested in the individual tab tests. test, but are instead tested in the individual tab tests.
""" """
# Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
# Block the GUI blocking thread # Block the GUI blocking thread
monkeypatch.setattr(GuiProjectSettings, "exec_", lambda *a: None) monkeypatch.setattr(GuiProjectSettings, "exec_", lambda *a: None)
monkeypatch.setattr(GuiProjectSettings, "result", lambda *a: QDialog.Accepted) monkeypatch.setattr(GuiProjectSettings, "result", lambda *a: QDialog.Accepted)
@@ -91,10 +85,6 @@ def testDlgProjSettings_Dialog(qtbot, monkeypatch, nwGUI):
def testDlgProjSettings_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mockRnd): def testDlgProjSettings_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mockRnd):
"""Test the main tab of the project settings dialog. """Test the main tab of the project settings dialog.
""" """
# Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
# Mock components # Mock components
monkeypatch.setattr(nwGUI.docEditor.spEnchant, "listDictionaries", lambda: [("en", "none")]) monkeypatch.setattr(nwGUI.docEditor.spEnchant, "listDictionaries", lambda: [("en", "none")])
@@ -125,23 +115,21 @@ def testDlgProjSettings_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mockRnd
assert tabMain.spellLang.currentData() == "en" assert tabMain.spellLang.currentData() == "en"
assert tabMain.doBackup.isChecked() is False assert tabMain.doBackup.isChecked() is False
qtbot.wait(stepDelay)
tabMain.editName.setText("") tabMain.editName.setText("")
for c in "Project Name": for c in "Project Name":
qtbot.keyClick(tabMain.editName, c, delay=typeDelay) qtbot.keyClick(tabMain.editName, c, delay=KEY_DELAY)
tabMain.editTitle.setText("") tabMain.editTitle.setText("")
for c in "Project Title": for c in "Project Title":
qtbot.keyClick(tabMain.editTitle, c, delay=typeDelay) qtbot.keyClick(tabMain.editTitle, c, delay=KEY_DELAY)
tabMain.editAuthors.clear() tabMain.editAuthors.clear()
for c in "Jane Doe": for c in "Jane Doe":
qtbot.keyClick(tabMain.editAuthors, c, delay=typeDelay) qtbot.keyClick(tabMain.editAuthors, c, delay=KEY_DELAY)
qtbot.keyClick(tabMain.editAuthors, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(tabMain.editAuthors, Qt.Key_Return, delay=KEY_DELAY)
for c in "John Doh": for c in "John Doh":
qtbot.keyClick(tabMain.editAuthors, c, delay=typeDelay) qtbot.keyClick(tabMain.editAuthors, c, delay=KEY_DELAY)
qtbot.keyClick(tabMain.editAuthors, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(tabMain.editAuthors, Qt.Key_Return, delay=KEY_DELAY)
qtbot.wait(stepDelay)
assert tabMain.editName.text() == "Project Name" assert tabMain.editName.text() == "Project Name"
assert tabMain.editTitle.text() == "Project Title" assert tabMain.editTitle.text() == "Project Title"
assert tabMain.editAuthors.toPlainText() == "Jane Doe\nJohn Doh\n" assert tabMain.editAuthors.toPlainText() == "Jane Doe\nJohn Doh\n"
@@ -164,9 +152,6 @@ def testDlgProjSettings_StatusImport(qtbot, monkeypatch, nwGUI, fncDir, fncProj,
"""Test the status and importance tabs of the project settings """Test the status and importance tabs of the project settings
dialog. dialog.
""" """
# Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True)) monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
# Mock components # Mock components
@@ -230,9 +215,9 @@ def testDlgProjSettings_StatusImport(qtbot, monkeypatch, nwGUI, fncDir, fncProj,
qtbot.mouseClick(tabStatus.addButton, Qt.LeftButton) qtbot.mouseClick(tabStatus.addButton, Qt.LeftButton)
tabStatus.listBox.setCurrentItem(tabStatus.listBox.topLevelItem(3)) tabStatus.listBox.setCurrentItem(tabStatus.listBox.topLevelItem(3))
for _ in range(8): for _ in range(8):
qtbot.keyClick(tabStatus.editName, Qt.Key_Backspace, delay=typeDelay) qtbot.keyClick(tabStatus.editName, Qt.Key_Backspace, delay=KEY_DELAY)
for c in "Final": for c in "Final":
qtbot.keyClick(tabStatus.editName, c, delay=typeDelay) qtbot.keyClick(tabStatus.editName, c, delay=KEY_DELAY)
qtbot.mouseClick(tabStatus.colButton, Qt.LeftButton) qtbot.mouseClick(tabStatus.colButton, Qt.LeftButton)
qtbot.mouseClick(tabStatus.saveButton, Qt.LeftButton) qtbot.mouseClick(tabStatus.saveButton, Qt.LeftButton)
assert tabStatus.listBox.topLevelItemCount() == 4 assert tabStatus.listBox.topLevelItemCount() == 4
@@ -310,9 +295,9 @@ def testDlgProjSettings_StatusImport(qtbot, monkeypatch, nwGUI, fncDir, fncProj,
tabImport.listBox.clearSelection() tabImport.listBox.clearSelection()
tabImport.listBox.setCurrentItem(tabImport.listBox.topLevelItem(3)) tabImport.listBox.setCurrentItem(tabImport.listBox.topLevelItem(3))
for _ in range(8): for _ in range(8):
qtbot.keyClick(tabImport.editName, Qt.Key_Backspace, delay=typeDelay) qtbot.keyClick(tabImport.editName, Qt.Key_Backspace, delay=KEY_DELAY)
for c in "Final": for c in "Final":
qtbot.keyClick(tabImport.editName, c, delay=typeDelay) qtbot.keyClick(tabImport.editName, c, delay=KEY_DELAY)
qtbot.mouseClick(tabImport.colButton, Qt.LeftButton) qtbot.mouseClick(tabImport.colButton, Qt.LeftButton)
qtbot.mouseClick(tabImport.saveButton, Qt.LeftButton) qtbot.mouseClick(tabImport.saveButton, Qt.LeftButton)
assert tabImport.listBox.topLevelItemCount() == 4 assert tabImport.listBox.topLevelItemCount() == 4
@@ -368,9 +353,6 @@ def testDlgProjSettings_StatusImport(qtbot, monkeypatch, nwGUI, fncDir, fncProj,
def testDlgProjSettings_Replace(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mockRnd): def testDlgProjSettings_Replace(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mockRnd):
"""Test the auto-replace tab of the project settings dialog. """Test the auto-replace tab of the project settings dialog.
""" """
# Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True)) monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
# Mock components # Mock components
@@ -419,10 +401,10 @@ def testDlgProjSettings_Replace(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mock
tabReplace.listBox.setCurrentItem(tabReplace.listBox.topLevelItem(2)) tabReplace.listBox.setCurrentItem(tabReplace.listBox.topLevelItem(2))
tabReplace.editKey.setText("") tabReplace.editKey.setText("")
for c in "Th is ": for c in "Th is ":
qtbot.keyClick(tabReplace.editKey, c, delay=typeDelay) qtbot.keyClick(tabReplace.editKey, c, delay=KEY_DELAY)
tabReplace.editValue.setText("") tabReplace.editValue.setText("")
for c in "With This Stuff ": for c in "With This Stuff ":
qtbot.keyClick(tabReplace.editValue, c, delay=typeDelay) qtbot.keyClick(tabReplace.editValue, c, delay=KEY_DELAY)
qtbot.mouseClick(tabReplace.saveButton, Qt.LeftButton) qtbot.mouseClick(tabReplace.saveButton, Qt.LeftButton)
assert tabReplace.listBox.topLevelItem(2).text(0) == "<This>" assert tabReplace.listBox.topLevelItem(2).text(0) == "<This>"
assert tabReplace.listBox.topLevelItem(2).text(1) == "With This Stuff " assert tabReplace.listBox.topLevelItem(2).text(1) == "With This Stuff "
+2 -12
View File
@@ -23,7 +23,7 @@ import os
import pytest import pytest
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QDialog, QMessageBox, QAction from PyQt5.QtWidgets import QDialog, QAction
from tools import writeFile, readFile, getGuiItem from tools import writeFile, readFile, getGuiItem
from mock import causeOSError from mock import causeOSError
@@ -31,25 +31,17 @@ from mock import causeOSError
from novelwriter.constants import nwFiles from novelwriter.constants import nwFiles
from novelwriter.dialogs.wordlist import GuiWordList from novelwriter.dialogs.wordlist import GuiWordList
keyDelay = 2
typeDelay = 1
stepDelay = 20
@pytest.mark.gui @pytest.mark.gui
def testDlgWordList_Dialog(qtbot, monkeypatch, nwGUI, nwMinimal): def testDlgWordList_Dialog(qtbot, monkeypatch, nwGUI, nwMinimal):
"""test the word list editor. """test the word list editor.
""" """
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(GuiWordList, "exec_", lambda *a: None) monkeypatch.setattr(GuiWordList, "exec_", lambda *a: None)
monkeypatch.setattr(GuiWordList, "result", lambda *a: QDialog.Accepted) monkeypatch.setattr(GuiWordList, "result", lambda *a: QDialog.Accepted)
monkeypatch.setattr(GuiWordList, "accept", lambda *a: None) monkeypatch.setattr(GuiWordList, "accept", lambda *a: None)
# Open project # Open project
nwGUI.openProject(nwMinimal) nwGUI.openProject(nwMinimal)
qtbot.wait(stepDelay)
dictFile = os.path.join(nwMinimal, "meta", nwFiles.PROJ_DICT) dictFile = os.path.join(nwMinimal, "meta", nwFiles.PROJ_DICT)
# Load the dialog # Load the dialog
@@ -59,7 +51,6 @@ def testDlgWordList_Dialog(qtbot, monkeypatch, nwGUI, nwMinimal):
wList = getGuiItem("GuiWordList") wList = getGuiItem("GuiWordList")
assert isinstance(wList, GuiWordList) assert isinstance(wList, GuiWordList)
wList.show() wList.show()
qtbot.wait(stepDelay)
# List should be blank # List should be blank
assert wList.listBox.count() == 0 assert wList.listBox.count() == 0
@@ -73,7 +64,6 @@ def testDlgWordList_Dialog(qtbot, monkeypatch, nwGUI, nwMinimal):
"word_f\n" "word_f\n"
"word_b\n" "word_b\n"
)) ))
qtbot.wait(stepDelay)
assert wList._loadWordList() assert wList._loadWordList()
# Check that the content was loaded # Check that the content was loaded
@@ -130,7 +120,7 @@ def testDlgWordList_Dialog(qtbot, monkeypatch, nwGUI, nwMinimal):
monkeypatch.setattr("builtins.open", causeOSError) monkeypatch.setattr("builtins.open", causeOSError)
assert not wList._doSave() assert not wList._doSave()
# qtbot.stopForInteraction() # qtbot.stop()
wList._doClose() wList._doClose()
# END Test testDlgWordList_Dialog # END Test testDlgWordList_Dialog
+24 -82
View File
@@ -25,33 +25,26 @@ from mock import causeOSError
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt
from PyQt5.QtGui import QTextBlock, QTextCursor, QTextOption from PyQt5.QtGui import QTextBlock, QTextCursor, QTextOption
from PyQt5.QtWidgets import QAction, QMessageBox, qApp from PyQt5.QtWidgets import QAction, qApp
from novelwriter.enum import nwDocAction, nwDocInsert, nwItemLayout from novelwriter.enum import nwDocAction, nwDocInsert, nwItemLayout
from novelwriter.constants import nwKeyWords, nwUnicode from novelwriter.constants import nwKeyWords, nwUnicode
from novelwriter.core.index import countWords from novelwriter.core.index import countWords
from novelwriter.gui.doceditor import GuiDocEditor from novelwriter.gui.doceditor import GuiDocEditor
keyDelay = 2 KEY_DELAY = 1
typeDelay = 1
stepDelay = 20
@pytest.mark.gui @pytest.mark.gui
def testGuiEditor_Init(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText): def testGuiEditor_Init(qtbot, nwGUI, nwMinimal, ipsumText):
"""Test initialising the editor. """Test initialising the editor.
""" """
# Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
# Open project # Open project
assert nwGUI.openProject(nwMinimal) assert nwGUI.openProject(nwMinimal)
assert nwGUI.openDocument("8c659a11cd429") assert nwGUI.openDocument("8c659a11cd429")
nwGUI.docEditor.setText("### Lorem Ipsum\n\n%s" % ipsumText[0]) nwGUI.docEditor.setText("### Lorem Ipsum\n\n%s" % ipsumText[0])
assert nwGUI.saveDocument() assert nwGUI.saveDocument()
qtbot.wait(stepDelay)
# Check Defaults # Check Defaults
qDoc = nwGUI.docEditor.document() qDoc = nwGUI.docEditor.document()
@@ -80,7 +73,7 @@ def testGuiEditor_Init(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
assert nwGUI.docEditor.horizontalScrollBarPolicy() == Qt.ScrollBarAlwaysOff assert nwGUI.docEditor.horizontalScrollBarPolicy() == Qt.ScrollBarAlwaysOff
assert nwGUI.docEditor._typPadChar == nwUnicode.U_THNBSP assert nwGUI.docEditor._typPadChar == nwUnicode.U_THNBSP
# qtbot.stopForInteraction() # qtbot.stop()
# END Test testGuiEditor_Init # END Test testGuiEditor_Init
@@ -89,10 +82,6 @@ def testGuiEditor_Init(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
def testGuiEditor_LoadText(qtbot, monkeypatch, caplog, nwGUI, nwMinimal, ipsumText): def testGuiEditor_LoadText(qtbot, monkeypatch, caplog, nwGUI, nwMinimal, ipsumText):
"""Test loading text into the editor. """Test loading text into the editor.
""" """
# Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
# Open project # Open project
sHandle = "8c659a11cd429" sHandle = "8c659a11cd429"
assert nwGUI.openProject(nwMinimal) is True assert nwGUI.openProject(nwMinimal) is True
@@ -102,7 +91,6 @@ def testGuiEditor_LoadText(qtbot, monkeypatch, caplog, nwGUI, nwMinimal, ipsumTe
nwGUI.docEditor.replaceText(longText) nwGUI.docEditor.replaceText(longText)
assert nwGUI.saveDocument() is True assert nwGUI.saveDocument() is True
assert nwGUI.closeDocument() is True assert nwGUI.closeDocument() is True
qtbot.wait(stepDelay)
# Load Text # Load Text
# ========= # =========
@@ -142,7 +130,7 @@ def testGuiEditor_LoadText(qtbot, monkeypatch, caplog, nwGUI, nwMinimal, ipsumTe
assert nwGUI.docEditor.loadText(sHandle) is True assert nwGUI.docEditor.loadText(sHandle) is True
assert nwGUI.docEditor.toPlainText() == "" assert nwGUI.docEditor.toPlainText() == ""
# qtbot.stopForInteraction() # qtbot.stop()
# END Test testGuiEditor_LoadText # END Test testGuiEditor_LoadText
@@ -151,15 +139,10 @@ def testGuiEditor_LoadText(qtbot, monkeypatch, caplog, nwGUI, nwMinimal, ipsumTe
def testGuiEditor_SaveText(qtbot, monkeypatch, caplog, nwGUI, nwMinimal, ipsumText): def testGuiEditor_SaveText(qtbot, monkeypatch, caplog, nwGUI, nwMinimal, ipsumText):
"""Test saving text from the editor. """Test saving text from the editor.
""" """
# Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
# Open project # Open project
sHandle = "8c659a11cd429" sHandle = "8c659a11cd429"
assert nwGUI.openProject(nwMinimal) is True assert nwGUI.openProject(nwMinimal) is True
assert nwGUI.openDocument(sHandle) is True assert nwGUI.openDocument(sHandle) is True
qtbot.wait(stepDelay)
# Save Text # Save Text
# ========= # =========
@@ -193,24 +176,19 @@ def testGuiEditor_SaveText(qtbot, monkeypatch, caplog, nwGUI, nwMinimal, ipsumTe
# Regular save # Regular save
assert nwGUI.docEditor.saveText() is True assert nwGUI.docEditor.saveText() is True
# qtbot.stopForInteraction() # qtbot.stop()
# END Test testGuiEditor_SaveText # END Test testGuiEditor_SaveText
@pytest.mark.gui @pytest.mark.gui
def testGuiEditor_MetaData(qtbot, monkeypatch, nwGUI, nwMinimal): def testGuiEditor_MetaData(qtbot, nwGUI, nwMinimal):
"""Test extracting various meta data and other values. """Test extracting various meta data and other values.
""" """
# Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
# Open project # Open project
sHandle = "8c659a11cd429" sHandle = "8c659a11cd429"
assert nwGUI.openProject(nwMinimal) is True assert nwGUI.openProject(nwMinimal) is True
assert nwGUI.openDocument(sHandle) is True assert nwGUI.openDocument(sHandle) is True
qtbot.wait(stepDelay)
# Get Text # Get Text
# This should replace line and paragraph separators, but preserve # This should replace line and paragraph separators, but preserve
@@ -253,21 +231,16 @@ def testGuiEditor_MetaData(qtbot, monkeypatch, nwGUI, nwMinimal):
@pytest.mark.gui @pytest.mark.gui
def testGuiEditor_Actions(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText): def testGuiEditor_Actions(qtbot, nwGUI, nwMinimal, ipsumText):
"""Test the document actions. This is not an extensive test of the """Test the document actions. This is not an extensive test of the
action features, just that the actions are actually called. The action features, just that the actions are actually called. The
various action features are tested when their respective functions various action features are tested when their respective functions
are tested. are tested.
""" """
# Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
# Open project # Open project
sHandle = "8c659a11cd429" sHandle = "8c659a11cd429"
assert nwGUI.openProject(nwMinimal) is True assert nwGUI.openProject(nwMinimal) is True
assert nwGUI.openDocument(sHandle) is True assert nwGUI.openDocument(sHandle) is True
qtbot.wait(stepDelay)
theText = "### A Scene\n\n%s" % "\n\n".join(ipsumText) theText = "### A Scene\n\n%s" % "\n\n".join(ipsumText)
assert nwGUI.docEditor.replaceText(theText) is True assert nwGUI.docEditor.replaceText(theText) is True
@@ -487,7 +460,7 @@ def testGuiEditor_Actions(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
# Unknown Action # Unknown Action
assert nwGUI.docEditor.docAction(nwDocAction.NO_ACTION) is False assert nwGUI.docEditor.docAction(nwDocAction.NO_ACTION) is False
# qtbot.stopForInteraction() # qtbot.stop()
# END Test testGuiEditor_Actions # END Test testGuiEditor_Actions
@@ -496,15 +469,10 @@ def testGuiEditor_Actions(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
def testGuiEditor_Insert(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText): def testGuiEditor_Insert(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
"""Test the document insert functions. """Test the document insert functions.
""" """
# Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
# Open project # Open project
sHandle = "8c659a11cd429" sHandle = "8c659a11cd429"
assert nwGUI.openProject(nwMinimal) is True assert nwGUI.openProject(nwMinimal) is True
assert nwGUI.openDocument(sHandle) is True assert nwGUI.openDocument(sHandle) is True
qtbot.wait(stepDelay)
theText = "### A Scene\n\n%s" % "\n\n".join(ipsumText) theText = "### A Scene\n\n%s" % "\n\n".join(ipsumText)
assert nwGUI.docEditor.replaceText(theText) is True assert nwGUI.docEditor.replaceText(theText) is True
@@ -577,7 +545,7 @@ def testGuiEditor_Insert(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
"\n\n\n", "\n\n@pov: Jane\n@char: John\n\n", 1 "\n\n\n", "\n\n@pov: Jane\n@char: John\n\n", 1
) )
# qtbot.stopForInteraction() # qtbot.stop()
# END Test testGuiEditor_Insert # END Test testGuiEditor_Insert
@@ -586,15 +554,10 @@ def testGuiEditor_Insert(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
def testGuiEditor_TextManipulation(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText): def testGuiEditor_TextManipulation(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
"""Test the text manipulation functions. """Test the text manipulation functions.
""" """
# Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
# Open project # Open project
sHandle = "8c659a11cd429" sHandle = "8c659a11cd429"
assert nwGUI.openProject(nwMinimal) is True assert nwGUI.openProject(nwMinimal) is True
assert nwGUI.openDocument(sHandle) is True assert nwGUI.openDocument(sHandle) is True
qtbot.wait(stepDelay)
theText = "### A Scene\n\n%s" % "\n\n".join(ipsumText) theText = "### A Scene\n\n%s" % "\n\n".join(ipsumText)
assert nwGUI.docEditor.replaceText(theText) is True assert nwGUI.docEditor.replaceText(theText) is True
@@ -791,7 +754,7 @@ def testGuiEditor_TextManipulation(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumTe
assert newPara[6] == twoBits[4] assert newPara[6] == twoBits[4]
assert newPara[7] == " ".join(twoBits[5:]) assert newPara[7] == " ".join(twoBits[5:])
# qtbot.stopForInteraction() # qtbot.stop()
# END Test testGuiEditor_TextManipulation # END Test testGuiEditor_TextManipulation
@@ -800,15 +763,10 @@ def testGuiEditor_TextManipulation(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumTe
def testGuiEditor_BlockFormatting(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText): def testGuiEditor_BlockFormatting(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
"""Test the block formatting function. """Test the block formatting function.
""" """
# Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
# Open project # Open project
sHandle = "8c659a11cd429" sHandle = "8c659a11cd429"
assert nwGUI.openProject(nwMinimal) is True assert nwGUI.openProject(nwMinimal) is True
assert nwGUI.openDocument(sHandle) is True assert nwGUI.openDocument(sHandle) is True
qtbot.wait(stepDelay)
theText = "### A Scene\n\n%s" % "\n\n".join(ipsumText) theText = "### A Scene\n\n%s" % "\n\n".join(ipsumText)
assert nwGUI.docEditor.replaceText(theText) is True assert nwGUI.docEditor.replaceText(theText) is True
@@ -1115,24 +1073,19 @@ def testGuiEditor_BlockFormatting(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumTex
assert nwGUI.docEditor._formatBlock(nwDocAction.BLOCK_COM) is True assert nwGUI.docEditor._formatBlock(nwDocAction.BLOCK_COM) is True
assert nwGUI.docEditor.getText() == "#### Title\n\n% The Text\n\n" assert nwGUI.docEditor.getText() == "#### Title\n\n% The Text\n\n"
# qtbot.stopForInteraction() # qtbot.stop()
# END Test testGuiEditor_BlockFormatting # END Test testGuiEditor_BlockFormatting
@pytest.mark.gui @pytest.mark.gui
def testGuiEditor_Tags(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText): def testGuiEditor_Tags(qtbot, nwGUI, nwMinimal, ipsumText):
"""Test the document editor tags functionality. """Test the document editor tags functionality.
""" """
# Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
# Open project # Open project
sHandle = "8c659a11cd429" sHandle = "8c659a11cd429"
assert nwGUI.openProject(nwMinimal) is True assert nwGUI.openProject(nwMinimal) is True
assert nwGUI.openDocument(sHandle) is True assert nwGUI.openDocument(sHandle) is True
qtbot.wait(stepDelay)
# Create Scene # Create Scene
theText = "### A Scene\n\n@char: Jane, John\n\n" + ipsumText[0] + "\n\n" theText = "### A Scene\n\n@char: Jane, John\n\n" + ipsumText[0] + "\n\n"
@@ -1181,7 +1134,7 @@ def testGuiEditor_Tags(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
assert nwGUI.closeDocViewer() is True assert nwGUI.closeDocViewer() is True
assert nwGUI.docViewer._docHandle is None assert nwGUI.docViewer._docHandle is None
# qtbot.stopForInteraction() # qtbot.stop()
# END Test testGuiEditor_Tags # END Test testGuiEditor_Tags
@@ -1190,10 +1143,6 @@ def testGuiEditor_Tags(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
def testGuiEditor_WordCounters(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText): def testGuiEditor_WordCounters(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
"""Test saving text from the editor. """Test saving text from the editor.
""" """
# Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
class MockThreadPool: class MockThreadPool:
def __init__(self): def __init__(self):
@@ -1226,7 +1175,6 @@ def testGuiEditor_WordCounters(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
nwGUI.theProject.tree[sHandle]._initCount = 0 # Clear item's count nwGUI.theProject.tree[sHandle]._initCount = 0 # Clear item's count
nwGUI.theProject.tree[sHandle]._wordCount = 0 # Clear item's count nwGUI.theProject.tree[sHandle]._wordCount = 0 # Clear item's count
assert nwGUI.openDocument(sHandle) is True assert nwGUI.openDocument(sHandle) is True
qtbot.wait(stepDelay)
theText = "\n\n".join(ipsumText) theText = "\n\n".join(ipsumText)
cC, wC, pC = countWords(theText) cC, wC, pC = countWords(theText)
@@ -1249,7 +1197,6 @@ def testGuiEditor_WordCounters(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
nwGUI.docEditor.wCounterDoc.run() nwGUI.docEditor.wCounterDoc.run()
# nwGUI.docEditor._updateDocCounts(cC, wC, pC) # nwGUI.docEditor._updateDocCounts(cC, wC, pC)
qtbot.wait(stepDelay)
assert nwGUI.theProject.tree[sHandle]._charCount == cC assert nwGUI.theProject.tree[sHandle]._charCount == cC
assert nwGUI.theProject.tree[sHandle]._wordCount == wC assert nwGUI.theProject.tree[sHandle]._wordCount == wC
assert nwGUI.theProject.tree[sHandle]._paraCount == pC assert nwGUI.theProject.tree[sHandle]._paraCount == pC
@@ -1258,7 +1205,6 @@ def testGuiEditor_WordCounters(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
# Select all text # Select all text
assert nwGUI.docEditor.docFooter._docSelection is False assert nwGUI.docEditor.docFooter._docSelection is False
nwGUI.docEditor.docAction(nwDocAction.SEL_ALL) nwGUI.docEditor.docAction(nwDocAction.SEL_ALL)
qtbot.wait(stepDelay)
assert nwGUI.docEditor.docFooter._docSelection is True assert nwGUI.docEditor.docFooter._docSelection is True
# Run the selection word counter # Run the selection word counter
@@ -1267,10 +1213,9 @@ def testGuiEditor_WordCounters(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
nwGUI.docEditor.wCounterSel.run() nwGUI.docEditor.wCounterSel.run()
# nwGUI.docEditor._updateSelCounts(cC, wC, pC) # nwGUI.docEditor._updateSelCounts(cC, wC, pC)
qtbot.wait(stepDelay)
assert nwGUI.docEditor.docFooter.wordsText.text() == f"Words: {wC} selected" assert nwGUI.docEditor.docFooter.wordsText.text() == f"Words: {wC} selected"
# qtbot.stopForInteraction() # qtbot.stop()
# END Test testGuiEditor_WordCounters # END Test testGuiEditor_WordCounters
@@ -1279,14 +1224,11 @@ def testGuiEditor_WordCounters(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, nwLipsum): def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, nwLipsum):
"""Test the document editor search functionality. """Test the document editor search functionality.
""" """
# Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(GuiDocEditor, "hasFocus", lambda *a: True) monkeypatch.setattr(GuiDocEditor, "hasFocus", lambda *a: True)
assert nwGUI.openProject(nwLipsum) is True assert nwGUI.openProject(nwLipsum) is True
assert nwGUI.openDocument("4c4f28287af27") is True assert nwGUI.openDocument("4c4f28287af27") is True
origText = nwGUI.docEditor.getText() origText = nwGUI.docEditor.getText()
qtbot.wait(stepDelay)
# Select the Word "est" # Select the Word "est"
nwGUI.docEditor.setCursorPosition(630) nwGUI.docEditor.setCursorPosition(630)
@@ -1301,11 +1243,11 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, nwLipsum):
# Find next by enter key # Find next by enter key
monkeypatch.setattr(nwGUI.docEditor.docSearch.searchBox, "hasFocus", lambda: True) monkeypatch.setattr(nwGUI.docEditor.docSearch.searchBox, "hasFocus", lambda: True)
qtbot.keyClick(nwGUI.docEditor.docSearch.searchBox, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor.docSearch.searchBox, Qt.Key_Return, delay=KEY_DELAY)
assert abs(nwGUI.docEditor.getCursorPosition() - 1284) < 3 assert abs(nwGUI.docEditor.getCursorPosition() - 1284) < 3
# Find next by button # Find next by button
qtbot.mouseClick(nwGUI.docEditor.docSearch.searchButton, Qt.LeftButton, delay=keyDelay) qtbot.mouseClick(nwGUI.docEditor.docSearch.searchButton, Qt.LeftButton, delay=KEY_DELAY)
assert abs(nwGUI.docEditor.getCursorPosition() - 1498) < 3 assert abs(nwGUI.docEditor.getCursorPosition() - 1498) < 3
# Activate loop search # Activate loop search
@@ -1323,14 +1265,14 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, nwLipsum):
assert nwGUI.docEditor.setCursorPosition(15) assert nwGUI.docEditor.setCursorPosition(15)
# Toggle search again with header button # Toggle search again with header button
qtbot.mouseClick(nwGUI.docEditor.docHeader.searchButton, Qt.LeftButton, delay=keyDelay) qtbot.mouseClick(nwGUI.docEditor.docHeader.searchButton, Qt.LeftButton, delay=KEY_DELAY)
assert nwGUI.docEditor.docSearch.setSearchText("") assert nwGUI.docEditor.docSearch.setSearchText("")
assert nwGUI.docEditor.docSearch.isVisible() is True assert nwGUI.docEditor.docSearch.isVisible() is True
# Search for non-existing # Search for non-existing
nwGUI.docEditor.setCursorPosition(0) nwGUI.docEditor.setCursorPosition(0)
assert nwGUI.docEditor.docSearch.setSearchText("abcdef") assert nwGUI.docEditor.docSearch.setSearchText("abcdef")
qtbot.mouseClick(nwGUI.docEditor.docSearch.searchButton, Qt.LeftButton, delay=keyDelay) qtbot.mouseClick(nwGUI.docEditor.docSearch.searchButton, Qt.LeftButton, delay=KEY_DELAY)
assert nwGUI.docEditor.getCursorPosition() < 3 # No result assert nwGUI.docEditor.getCursorPosition() < 3 # No result
# Enable RegEx search # Enable RegEx search
@@ -1341,19 +1283,19 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, nwLipsum):
# Set invalid RegEx # Set invalid RegEx
nwGUI.docEditor.setCursorPosition(0) nwGUI.docEditor.setCursorPosition(0)
assert nwGUI.docEditor.docSearch.setSearchText(r"\bSus[") assert nwGUI.docEditor.docSearch.setSearchText(r"\bSus[")
qtbot.mouseClick(nwGUI.docEditor.docSearch.searchButton, Qt.LeftButton, delay=keyDelay) qtbot.mouseClick(nwGUI.docEditor.docSearch.searchButton, Qt.LeftButton, delay=KEY_DELAY)
assert nwGUI.docEditor.getCursorPosition() < 3 # No result assert nwGUI.docEditor.getCursorPosition() < 3 # No result
# Set dangerous RegEx (issue #1015) # Set dangerous RegEx (issue #1015)
# If this doesn't get caught, the app will hang # If this doesn't get caught, the app will hang
nwGUI.docEditor.setCursorPosition(0) nwGUI.docEditor.setCursorPosition(0)
assert nwGUI.docEditor.docSearch.setSearchText(r".*") assert nwGUI.docEditor.docSearch.setSearchText(r".*")
qtbot.mouseClick(nwGUI.docEditor.docSearch.searchButton, Qt.LeftButton, delay=keyDelay) qtbot.mouseClick(nwGUI.docEditor.docSearch.searchButton, Qt.LeftButton, delay=KEY_DELAY)
assert abs(nwGUI.docEditor.getCursorPosition() - 14) < 3 assert abs(nwGUI.docEditor.getCursorPosition() - 14) < 3
# Set valid RegEx # Set valid RegEx
assert nwGUI.docEditor.docSearch.setSearchText(r"\bSus") assert nwGUI.docEditor.docSearch.setSearchText(r"\bSus")
qtbot.mouseClick(nwGUI.docEditor.docSearch.searchButton, Qt.LeftButton, delay=keyDelay) qtbot.mouseClick(nwGUI.docEditor.docSearch.searchButton, Qt.LeftButton, delay=KEY_DELAY)
assert abs(nwGUI.docEditor.getCursorPosition() - 208) < 3 assert abs(nwGUI.docEditor.getCursorPosition() - 208) < 3
# Find next and then prev # Find next and then prev
@@ -1404,7 +1346,7 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, nwLipsum):
assert abs(nwGUI.docEditor.getCursorPosition() - 208) < 3 assert abs(nwGUI.docEditor.getCursorPosition() - 208) < 3
# Replace "sus" with "foo" via replace button # Replace "sus" with "foo" via replace button
qtbot.mouseClick(nwGUI.docEditor.docSearch.replaceButton, Qt.LeftButton, delay=keyDelay) qtbot.mouseClick(nwGUI.docEditor.docSearch.replaceButton, Qt.LeftButton, delay=KEY_DELAY)
assert nwGUI.docEditor.getText()[205:213] == "foocipit" assert nwGUI.docEditor.getText()[205:213] == "foocipit"
# Revert last two replaces # Revert last two replaces
@@ -1482,7 +1424,7 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, nwLipsum):
monkeypatch.setattr(nwGUI.docEditor.docSearch.replaceBox, "hasFocus", lambda: True) monkeypatch.setattr(nwGUI.docEditor.docSearch.replaceBox, "hasFocus", lambda: True)
assert nwGUI.docEditor.focusNextPrevChild(True) is True assert nwGUI.docEditor.focusNextPrevChild(True) is True
# qtbot.stopForInteraction() # qtbot.stop()
# END Test testGuiEditor_Search # END Test testGuiEditor_Search
+2 -10
View File
@@ -23,25 +23,17 @@ import pytest
from PyQt5.QtCore import Qt, QUrl from PyQt5.QtCore import Qt, QUrl
from PyQt5.QtGui import QTextCursor from PyQt5.QtGui import QTextCursor
from PyQt5.QtWidgets import qApp, QAction, QMessageBox from PyQt5.QtWidgets import qApp, QAction
from mock import causeException from mock import causeException
from novelwriter.enum import nwDocAction from novelwriter.enum import nwDocAction
from novelwriter.core.tohtml import ToHtml from novelwriter.core.tohtml import ToHtml
keyDelay = 2
typeDelay = 1
stepDelay = 20
@pytest.mark.gui @pytest.mark.gui
def testGuiViewer_Main(qtbot, monkeypatch, nwGUI, nwLipsum): def testGuiViewer_Main(qtbot, monkeypatch, nwGUI, nwLipsum):
"""Test the document viewer. """Test the document viewer.
""" """
# Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
# Open project # Open project
assert nwGUI.openProject(nwLipsum) assert nwGUI.openProject(nwLipsum)
@@ -184,6 +176,6 @@ def testGuiViewer_Main(qtbot, monkeypatch, nwGUI, nwLipsum):
assert nwGUI.docViewer.loadText("846352075de7d") is False assert nwGUI.docViewer.loadText("846352075de7d") is False
assert nwGUI.docViewer.toPlainText() == "An error occurred while generating the preview." assert nwGUI.docViewer.toPlainText() == "An error occurred while generating the preview."
# qtbot.stopForInteraction() # qtbot.stop()
# END Test testGuiViewer_Main # END Test testGuiViewer_Main
+120 -142
View File
@@ -37,17 +37,13 @@ from novelwriter.gui.projtree import GuiProjectTree
from novelwriter.gui.doceditor import GuiDocEditor from novelwriter.gui.doceditor import GuiDocEditor
from novelwriter.gui.noveltree import GuiNovelView from novelwriter.gui.noveltree import GuiNovelView
keyDelay = 2 KEY_DELAY = 1
typeDelay = 1
stepDelay = 20
@pytest.mark.gui @pytest.mark.gui
def testGuiMain_ProjectBlocker(monkeypatch, nwGUI): def testGuiMain_ProjectBlocker(nwGUI):
"""Test the blocking of features when there's no project open. """Test the blocking of features when there's no project open.
""" """
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
# Test no-project blocking # Test no-project blocking
assert nwGUI.closeProject() is True assert nwGUI.closeProject() is True
assert nwGUI.saveProject() is False assert nwGUI.saveProject() is False
@@ -74,9 +70,6 @@ def testGuiMain_ProjectBlocker(monkeypatch, nwGUI):
def testGuiMain_NewProject(monkeypatch, nwGUI, fncProj): def testGuiMain_NewProject(monkeypatch, nwGUI, fncProj):
"""Test creating a new project. """Test creating a new project.
""" """
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
# No data # No data
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr(GuiProjectWizard, "exec_", lambda *a: None) mp.setattr(GuiProjectWizard, "exec_", lambda *a: None)
@@ -113,8 +106,6 @@ def testGuiMain_NewProject(monkeypatch, nwGUI, fncProj):
def testGuiMain_ProjectTreeItems(qtbot, monkeypatch, nwGUI, fncProj, mockRnd): def testGuiMain_ProjectTreeItems(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
"""Test handling of project tree items based on GUI focus states. """Test handling of project tree items based on GUI focus states.
""" """
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
buildTestProject(nwGUI, fncProj) buildTestProject(nwGUI, fncProj)
sHandle = "000000000000f" sHandle = "000000000000f"
@@ -165,9 +156,6 @@ def testGuiMain_ProjectTreeItems(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mockRnd): def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mockRnd):
"""Test the document editor. """Test the document editor.
""" """
# Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: True) monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: True)
monkeypatch.setattr(GuiDocEditor, "hasFocus", lambda *a: True) monkeypatch.setattr(GuiDocEditor, "hasFocus", lambda *a: True)
monkeypatch.setattr(QInputDialog, "getText", lambda *a, text: (text, True)) monkeypatch.setattr(QInputDialog, "getText", lambda *a, text: (text, True))
@@ -195,11 +183,9 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
compFile = os.path.join(refDir, "guiEditor_Main_Initial_nwProject.nwx") compFile = os.path.join(refDir, "guiEditor_Main_Initial_nwProject.nwx")
copyfile(projFile, testFile) copyfile(projFile, testFile)
assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE) assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE)
qtbot.wait(stepDelay)
# Re-open project # Re-open project
assert nwGUI.openProject(fncProj) assert nwGUI.openProject(fncProj)
qtbot.wait(stepDelay)
# Check that we loaded the data # Check that we loaded the data
assert len(nwGUI.theProject.tree) == 8 assert len(nwGUI.theProject.tree) == 8
@@ -241,18 +227,18 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
# Type something into the document # Type something into the document
nwGUI.switchFocus(nwWidget.EDITOR) nwGUI.switchFocus(nwWidget.EDITOR)
qtbot.keyClick(nwGUI.docEditor, "a", modifier=Qt.ControlModifier, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, "a", modifier=Qt.ControlModifier, delay=KEY_DELAY)
for c in "# Jane Doe": for c in "# Jane Doe":
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in "@tag: Jane": for c in "@tag: Jane":
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in "This is a file about Jane.": for c in "This is a file about Jane.":
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
# Add a Plot File # Add a Plot File
nwGUI.switchFocus(nwWidget.TREE) nwGUI.switchFocus(nwWidget.TREE)
@@ -263,18 +249,18 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
# Type something into the document # Type something into the document
nwGUI.switchFocus(nwWidget.EDITOR) nwGUI.switchFocus(nwWidget.EDITOR)
qtbot.keyClick(nwGUI.docEditor, "a", modifier=Qt.ControlModifier, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, "a", modifier=Qt.ControlModifier, delay=KEY_DELAY)
for c in "# Main Plot": for c in "# Main Plot":
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in "@tag: MainPlot": for c in "@tag: MainPlot":
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in "This is a file detailing the main plot.": for c in "This is a file detailing the main plot.":
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
# Add a World File # Add a World File
nwGUI.switchFocus(nwWidget.TREE) nwGUI.switchFocus(nwWidget.TREE)
@@ -290,18 +276,18 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
# Type something into the document # Type something into the document
nwGUI.switchFocus(nwWidget.EDITOR) nwGUI.switchFocus(nwWidget.EDITOR)
qtbot.keyClick(nwGUI.docEditor, "a", modifier=Qt.ControlModifier, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, "a", modifier=Qt.ControlModifier, delay=KEY_DELAY)
for c in "# Main Location": for c in "# Main Location":
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in "@tag: Home": for c in "@tag: Home":
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in "This is a file describing Jane's home.": for c in "This is a file describing Jane's home.":
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
# Trigger autosaves before making more changes # Trigger autosaves before making more changes
nwGUI._autoSaveDocument() nwGUI._autoSaveDocument()
@@ -317,67 +303,67 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
# Type something into the document # Type something into the document
nwGUI.switchFocus(nwWidget.EDITOR) nwGUI.switchFocus(nwWidget.EDITOR)
qtbot.keyClick(nwGUI.docEditor, "a", modifier=Qt.ControlModifier, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, "a", modifier=Qt.ControlModifier, delay=KEY_DELAY)
for c in "# Novel": for c in "# Novel":
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in "## Chapter": for c in "## Chapter":
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in "@pov: Jane": for c in "@pov: Jane":
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in "@plot: MainPlot": for c in "@plot: MainPlot":
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in "### Scene": for c in "### Scene":
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in "% How about a comment?": for c in "% How about a comment?":
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in "@pov: Jane": for c in "@pov: Jane":
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in "@plot: MainPlot": for c in "@plot: MainPlot":
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in "@location: Home": for c in "@location: Home":
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in "#### Some Section": for c in "#### Some Section":
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in "@char: Jane": for c in "@char: Jane":
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in "This is a paragraph of nonsense text.": for c in "This is a paragraph of nonsense text.":
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
# Don't allow Shift+Enter to insert a line separator (issue #1150) # Don't allow Shift+Enter to insert a line separator (issue #1150)
for c in "This is another paragraph": for c in "This is another paragraph":
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Enter, modifier=Qt.ShiftModifier, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Enter, modifier=Qt.ShiftModifier, delay=KEY_DELAY)
for c in "with a line separator in it.": for c in "with a line separator in it.":
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
# Auto-Replace # Auto-Replace
# ============ # ============
@@ -386,43 +372,43 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
"This is another paragraph of much longer nonsense text. " "This is another paragraph of much longer nonsense text. "
"It is in fact 1 very very NONSENSICAL nonsense text! " "It is in fact 1 very very NONSENSICAL nonsense text! "
): ):
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
for c in "We can also try replacing \"quotes\", even single 'quotes' are replaced. ": for c in "We can also try replacing \"quotes\", even single 'quotes' are replaced. ":
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
for c in "Isn't that nice? ": for c in "Isn't that nice? ":
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
for c in "We can hyphen-ate, make dashes -- and even longer dashes --- if we want. ": for c in "We can hyphen-ate, make dashes -- and even longer dashes --- if we want. ":
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
for c in "Ellipsis? Not a problem either ... ": for c in "Ellipsis? Not a problem either ... ":
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
for c in "How about three hyphens - -": for c in "How about three hyphens - -":
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Left, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Left, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Backspace, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Backspace, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Right, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Right, delay=KEY_DELAY)
for c in "- for long dash? It works too.": for c in "- for long dash? It works too.":
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in "\"Full line double quoted text.\"": for c in "\"Full line double quoted text.\"":
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in "'Full line single quoted text.'": for c in "'Full line single quoted text.'":
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
# Insert spaces before and after quotes # Insert spaces before and after quotes
nwGUI.mainConf.fmtPadBefore = "\u201d" nwGUI.mainConf.fmtPadBefore = "\u201d"
nwGUI.mainConf.fmtPadAfter = "\u201c" nwGUI.mainConf.fmtPadAfter = "\u201c"
for c in "Some \"double quoted text with spaces padded\".": for c in "Some \"double quoted text with spaces padded\".":
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
nwGUI.mainConf.fmtPadBefore = "" nwGUI.mainConf.fmtPadBefore = ""
nwGUI.mainConf.fmtPadAfter = "" nwGUI.mainConf.fmtPadAfter = ""
@@ -431,24 +417,24 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
nwGUI.mainConf.fmtPadBefore = ":" nwGUI.mainConf.fmtPadBefore = ":"
for c in "@object: NoSpaceAdded": for c in "@object: NoSpaceAdded":
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in "% synopsis: No space before this colon.": for c in "% synopsis: No space before this colon.":
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in "Add space before this colon: See?": for c in "Add space before this colon: See?":
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in "But don't add a double space : See?": for c in "But don't add a double space : See?":
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
nwGUI.mainConf.fmtPadBefore = "" nwGUI.mainConf.fmtPadBefore = ""
@@ -456,55 +442,49 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
# ================ # ================
for c in "\t\"Tab-indented text\"": for c in "\t\"Tab-indented text\"":
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in ">\"Paragraph-indented text\"": for c in ">\"Paragraph-indented text\"":
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in ">>\"Right-aligned text\"": for c in ">>\"Right-aligned text\"":
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in "\t'Tab-indented text'": for c in "\t'Tab-indented text'":
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in ">'Paragraph-indented text'": for c in ">'Paragraph-indented text'":
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
for c in ">>'Right-aligned text'": for c in ">>'Right-aligned text'":
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, c, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=KEY_DELAY)
qtbot.wait(stepDelay)
nwGUI.docEditor.wCounterDoc.run() nwGUI.docEditor.wCounterDoc.run()
qtbot.wait(stepDelay)
# Save the document # Save the document
assert nwGUI.docEditor.docChanged() assert nwGUI.docEditor.docChanged()
assert nwGUI.saveDocument() assert nwGUI.saveDocument()
assert not nwGUI.docEditor.docChanged() assert not nwGUI.docEditor.docChanged()
qtbot.wait(stepDelay)
nwGUI.rebuildIndex() nwGUI.rebuildIndex()
qtbot.wait(stepDelay)
# Open and view the edited document # Open and view the edited document
nwGUI.switchFocus(nwWidget.VIEWER) nwGUI.switchFocus(nwWidget.VIEWER)
assert nwGUI.openDocument(C.hSceneDoc) assert nwGUI.openDocument(C.hSceneDoc)
assert nwGUI.viewDocument(C.hSceneDoc) assert nwGUI.viewDocument(C.hSceneDoc)
qtbot.wait(stepDelay)
assert nwGUI.saveProject() assert nwGUI.saveProject()
assert nwGUI.closeDocViewer() assert nwGUI.closeDocViewer()
qtbot.wait(stepDelay)
# Check a Quick Create and Delete # Check a Quick Create and Delete
assert nwGUI.projView.projTree.newTreeItem(nwItemType.FILE, None) assert nwGUI.projView.projTree.newTreeItem(nwItemType.FILE, None)
@@ -553,11 +533,9 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
@pytest.mark.gui @pytest.mark.gui
def testGuiMain_FocusFullMode(qtbot, monkeypatch, nwGUI, fncProj, mockRnd): def testGuiMain_FocusFullMode(qtbot, nwGUI, fncProj, mockRnd):
"""Test toggling focus mode in main window. """Test toggling focus mode in main window.
""" """
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
buildTestProject(nwGUI, fncProj) buildTestProject(nwGUI, fncProj)
assert nwGUI.isFocusMode is False assert nwGUI.isFocusMode is False
@@ -596,6 +574,6 @@ def testGuiMain_FocusFullMode(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
nwGUI.toggleFullScreenMode() nwGUI.toggleFullScreenMode()
assert nwGUI.mainConf.isFullScreen is False assert nwGUI.mainConf.isFullScreen is False
# qtbot.stopForInteraction() # qtbot.stop()
# END Test testGuiMain_FocusFullMode # END Test testGuiMain_FocusFullMode
+9 -55
View File
@@ -32,24 +32,17 @@ from novelwriter.enum import nwDocAction, nwDocInsert
from novelwriter.constants import nwKeyWords, nwUnicode from novelwriter.constants import nwKeyWords, nwUnicode
from novelwriter.gui.doceditor import GuiDocEditor from novelwriter.gui.doceditor import GuiDocEditor
keyDelay = 2
typeDelay = 1
stepDelay = 20
@pytest.mark.gui @pytest.mark.gui
def testGuiMenu_EditFormat(qtbot, monkeypatch, nwGUI, nwLipsum): def testGuiMenu_EditFormat(qtbot, monkeypatch, nwGUI, nwLipsum):
"""Test the main menu Edit and Format entries. """Test the main menu Edit and Format entries.
""" """
# Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(GuiDocEditor, "hasFocus", lambda *a: True) monkeypatch.setattr(GuiDocEditor, "hasFocus", lambda *a: True)
# Test Document Action with No Project # Test Document Action with No Project
assert nwGUI.docEditor.docAction(nwDocAction.COPY) is False assert nwGUI.docEditor.docAction(nwDocAction.COPY) is False
assert nwGUI.openProject(nwLipsum) is True assert nwGUI.openProject(nwLipsum) is True
qtbot.wait(stepDelay)
# Split By Chapter # Split By Chapter
assert nwGUI.openDocument("4c4f28287af27") is True assert nwGUI.openDocument("4c4f28287af27") is True
@@ -61,57 +54,43 @@ def testGuiMenu_EditFormat(qtbot, monkeypatch, nwGUI, nwLipsum):
nwGUI.mainMenu.aFmtStrong.activate(QAction.Trigger) nwGUI.mainMenu.aFmtStrong.activate(QAction.Trigger)
fmtStr = "**Pellentesque** nec erat ut nulla posuere commodo." fmtStr = "**Pellentesque** nec erat ut nulla posuere commodo."
assert nwGUI.docEditor.getText()[39:90] == fmtStr assert nwGUI.docEditor.getText()[39:90] == fmtStr
qtbot.wait(stepDelay)
nwGUI.mainMenu.aFmtStrong.activate(QAction.Trigger) nwGUI.mainMenu.aFmtStrong.activate(QAction.Trigger)
assert nwGUI.docEditor.getText()[39:86] == cleanText assert nwGUI.docEditor.getText()[39:86] == cleanText
qtbot.wait(stepDelay)
# Italic # Italic
nwGUI.mainMenu.aFmtEmph.activate(QAction.Trigger) nwGUI.mainMenu.aFmtEmph.activate(QAction.Trigger)
fmtStr = "_Pellentesque_ nec erat ut nulla posuere commodo." fmtStr = "_Pellentesque_ nec erat ut nulla posuere commodo."
assert nwGUI.docEditor.getText()[39:88] == fmtStr assert nwGUI.docEditor.getText()[39:88] == fmtStr
qtbot.wait(stepDelay)
nwGUI.mainMenu.aFmtEmph.activate(QAction.Trigger) nwGUI.mainMenu.aFmtEmph.activate(QAction.Trigger)
assert nwGUI.docEditor.getText()[39:86] == cleanText assert nwGUI.docEditor.getText()[39:86] == cleanText
qtbot.wait(stepDelay)
# Strikethrough # Strikethrough
nwGUI.mainMenu.aFmtStrike.activate(QAction.Trigger) nwGUI.mainMenu.aFmtStrike.activate(QAction.Trigger)
fmtStr = "~~Pellentesque~~ nec erat ut nulla posuere commodo." fmtStr = "~~Pellentesque~~ nec erat ut nulla posuere commodo."
assert nwGUI.docEditor.getText()[39:90] == fmtStr assert nwGUI.docEditor.getText()[39:90] == fmtStr
qtbot.wait(stepDelay)
nwGUI.mainMenu.aFmtStrike.activate(QAction.Trigger) nwGUI.mainMenu.aFmtStrike.activate(QAction.Trigger)
assert nwGUI.docEditor.getText()[39:86] == cleanText assert nwGUI.docEditor.getText()[39:86] == cleanText
qtbot.wait(stepDelay)
# Should get us back to plain # Should get us back to plain
nwGUI.mainMenu.aFmtStrong.activate(QAction.Trigger) nwGUI.mainMenu.aFmtStrong.activate(QAction.Trigger)
qtbot.wait(stepDelay)
nwGUI.mainMenu.aFmtEmph.activate(QAction.Trigger) nwGUI.mainMenu.aFmtEmph.activate(QAction.Trigger)
qtbot.wait(stepDelay)
nwGUI.mainMenu.aFmtEmph.activate(QAction.Trigger) nwGUI.mainMenu.aFmtEmph.activate(QAction.Trigger)
qtbot.wait(stepDelay)
nwGUI.mainMenu.aFmtStrong.activate(QAction.Trigger) nwGUI.mainMenu.aFmtStrong.activate(QAction.Trigger)
assert nwGUI.docEditor.getText()[39:86] == cleanText assert nwGUI.docEditor.getText()[39:86] == cleanText
qtbot.wait(stepDelay)
# Double Quotes # Double Quotes
nwGUI.mainMenu.aFmtDQuote.activate(QAction.Trigger) nwGUI.mainMenu.aFmtDQuote.activate(QAction.Trigger)
fmtStr = "“Pellentesque” nec erat ut nulla posuere commodo." fmtStr = "“Pellentesque” nec erat ut nulla posuere commodo."
assert nwGUI.docEditor.getText()[39:88] == fmtStr assert nwGUI.docEditor.getText()[39:88] == fmtStr
qtbot.wait(stepDelay)
nwGUI.mainMenu.aEditUndo.activate(QAction.Trigger) nwGUI.mainMenu.aEditUndo.activate(QAction.Trigger)
assert nwGUI.docEditor.getText()[39:86] == cleanText assert nwGUI.docEditor.getText()[39:86] == cleanText
qtbot.wait(stepDelay)
# Single Quotes # Single Quotes
nwGUI.mainMenu.aFmtSQuote.activate(QAction.Trigger) nwGUI.mainMenu.aFmtSQuote.activate(QAction.Trigger)
fmtStr = "Pellentesque nec erat ut nulla posuere commodo." fmtStr = "Pellentesque nec erat ut nulla posuere commodo."
assert nwGUI.docEditor.getText()[39:88] == fmtStr assert nwGUI.docEditor.getText()[39:88] == fmtStr
qtbot.wait(stepDelay)
nwGUI.mainMenu.aEditUndo.activate(QAction.Trigger) nwGUI.mainMenu.aEditUndo.activate(QAction.Trigger)
assert nwGUI.docEditor.getText()[39:86] == cleanText assert nwGUI.docEditor.getText()[39:86] == cleanText
qtbot.wait(stepDelay)
# Block Formats # Block Formats
# ============= # =============
@@ -121,61 +100,50 @@ def testGuiMenu_EditFormat(qtbot, monkeypatch, nwGUI, nwLipsum):
nwGUI.mainMenu.aFmtHead1.activate(QAction.Trigger) nwGUI.mainMenu.aFmtHead1.activate(QAction.Trigger)
fmtStr = "# Pellentesque nec erat ut nulla posuere commodo." fmtStr = "# Pellentesque nec erat ut nulla posuere commodo."
assert nwGUI.docEditor.getText()[39:88] == fmtStr assert nwGUI.docEditor.getText()[39:88] == fmtStr
qtbot.wait(stepDelay)
# Header 2 # Header 2
nwGUI.mainMenu.aFmtHead2.activate(QAction.Trigger) nwGUI.mainMenu.aFmtHead2.activate(QAction.Trigger)
fmtStr = "## Pellentesque nec erat ut nulla posuere commodo." fmtStr = "## Pellentesque nec erat ut nulla posuere commodo."
assert nwGUI.docEditor.getText()[39:89] == fmtStr assert nwGUI.docEditor.getText()[39:89] == fmtStr
qtbot.wait(stepDelay)
# Header 3 # Header 3
nwGUI.mainMenu.aFmtHead3.activate(QAction.Trigger) nwGUI.mainMenu.aFmtHead3.activate(QAction.Trigger)
fmtStr = "### Pellentesque nec erat ut nulla posuere commodo." fmtStr = "### Pellentesque nec erat ut nulla posuere commodo."
assert nwGUI.docEditor.getText()[39:90] == fmtStr assert nwGUI.docEditor.getText()[39:90] == fmtStr
qtbot.wait(stepDelay)
# Header 4 # Header 4
nwGUI.mainMenu.aFmtHead4.activate(QAction.Trigger) nwGUI.mainMenu.aFmtHead4.activate(QAction.Trigger)
fmtStr = "#### Pellentesque nec erat ut nulla posuere commodo." fmtStr = "#### Pellentesque nec erat ut nulla posuere commodo."
assert nwGUI.docEditor.getText()[39:91] == fmtStr assert nwGUI.docEditor.getText()[39:91] == fmtStr
qtbot.wait(stepDelay)
# Clear Format # Clear Format
nwGUI.mainMenu.aFmtNoFormat.activate(QAction.Trigger) nwGUI.mainMenu.aFmtNoFormat.activate(QAction.Trigger)
assert nwGUI.docEditor.getText()[39:86] == cleanText assert nwGUI.docEditor.getText()[39:86] == cleanText
qtbot.wait(stepDelay)
# Comment On # Comment On
nwGUI.mainMenu.aFmtComment.activate(QAction.Trigger) nwGUI.mainMenu.aFmtComment.activate(QAction.Trigger)
fmtStr = "% Pellentesque nec erat ut nulla posuere commodo." fmtStr = "% Pellentesque nec erat ut nulla posuere commodo."
assert nwGUI.docEditor.getText()[39:88] == fmtStr assert nwGUI.docEditor.getText()[39:88] == fmtStr
qtbot.wait(stepDelay)
# Comment Off # Comment Off
nwGUI.mainMenu.aFmtComment.activate(QAction.Trigger) nwGUI.mainMenu.aFmtComment.activate(QAction.Trigger)
assert nwGUI.docEditor.getText()[39:86] == cleanText assert nwGUI.docEditor.getText()[39:86] == cleanText
qtbot.wait(stepDelay)
# Check comment with no space before text # Check comment with no space before text
assert nwGUI.docEditor.setCursorPosition(39) assert nwGUI.docEditor.setCursorPosition(39)
assert nwGUI.docEditor.insertText("%") assert nwGUI.docEditor.insertText("%")
fmtStr = "%Pellentesque nec erat ut nulla posuere commodo." fmtStr = "%Pellentesque nec erat ut nulla posuere commodo."
assert nwGUI.docEditor.getText()[39:87] == fmtStr assert nwGUI.docEditor.getText()[39:87] == fmtStr
qtbot.wait(stepDelay)
nwGUI.mainMenu.aFmtNoFormat.activate(QAction.Trigger) nwGUI.mainMenu.aFmtNoFormat.activate(QAction.Trigger)
assert nwGUI.docEditor.getText()[39:86] == cleanText assert nwGUI.docEditor.getText()[39:86] == cleanText
qtbot.wait(stepDelay)
# Undo/Redo # Undo/Redo
nwGUI.mainMenu.aEditUndo.activate(QAction.Trigger) nwGUI.mainMenu.aEditUndo.activate(QAction.Trigger)
fmtStr = "%Pellentesque nec erat ut nulla posuere commodo." fmtStr = "%Pellentesque nec erat ut nulla posuere commodo."
assert nwGUI.docEditor.getText()[39:87] == fmtStr assert nwGUI.docEditor.getText()[39:87] == fmtStr
qtbot.wait(stepDelay)
nwGUI.mainMenu.aEditRedo.activate(QAction.Trigger) nwGUI.mainMenu.aEditRedo.activate(QAction.Trigger)
assert nwGUI.docEditor.getText()[39:86] == cleanText assert nwGUI.docEditor.getText()[39:86] == cleanText
qtbot.wait(stepDelay)
# Cut, Copy and Paste # Cut, Copy and Paste
assert nwGUI.docEditor.setCursorPosition(39) assert nwGUI.docEditor.setCursorPosition(39)
@@ -240,36 +208,30 @@ def testGuiMenu_EditFormat(qtbot, monkeypatch, nwGUI, nwLipsum):
nwGUI.mainMenu.aFmtAlignLeft.activate(QAction.Trigger) nwGUI.mainMenu.aFmtAlignLeft.activate(QAction.Trigger)
fmtStr = "A single, short paragraph. <<" fmtStr = "A single, short paragraph. <<"
assert nwGUI.docEditor.getText()[:29] == fmtStr assert nwGUI.docEditor.getText()[:29] == fmtStr
qtbot.wait(stepDelay)
# Right Align # Right Align
nwGUI.mainMenu.aFmtAlignRight.activate(QAction.Trigger) nwGUI.mainMenu.aFmtAlignRight.activate(QAction.Trigger)
fmtStr = ">> A single, short paragraph." fmtStr = ">> A single, short paragraph."
assert nwGUI.docEditor.getText()[:29] == fmtStr assert nwGUI.docEditor.getText()[:29] == fmtStr
qtbot.wait(stepDelay)
# Centre Align # Centre Align
nwGUI.mainMenu.aFmtAlignCentre.activate(QAction.Trigger) nwGUI.mainMenu.aFmtAlignCentre.activate(QAction.Trigger)
fmtStr = ">> A single, short paragraph. <<" fmtStr = ">> A single, short paragraph. <<"
assert nwGUI.docEditor.getText()[:32] == fmtStr assert nwGUI.docEditor.getText()[:32] == fmtStr
qtbot.wait(stepDelay)
# Left Indent # Left Indent
nwGUI.mainMenu.aFmtIndentLeft.activate(QAction.Trigger) nwGUI.mainMenu.aFmtIndentLeft.activate(QAction.Trigger)
fmtStr = "> A single, short paragraph." fmtStr = "> A single, short paragraph."
assert nwGUI.docEditor.getText()[:28] == fmtStr assert nwGUI.docEditor.getText()[:28] == fmtStr
qtbot.wait(stepDelay)
# Right Indent # Right Indent
nwGUI.mainMenu.aFmtIndentRight.activate(QAction.Trigger) nwGUI.mainMenu.aFmtIndentRight.activate(QAction.Trigger)
fmtStr = "> A single, short paragraph. <" fmtStr = "> A single, short paragraph. <"
assert nwGUI.docEditor.getText()[:30] == fmtStr assert nwGUI.docEditor.getText()[:30] == fmtStr
qtbot.wait(stepDelay)
# No Format # No Format
nwGUI.mainMenu.aFmtNoFormat.activate(QAction.Trigger) nwGUI.mainMenu.aFmtNoFormat.activate(QAction.Trigger)
assert nwGUI.docEditor.getText()[:30] == cleanText assert nwGUI.docEditor.getText()[:30] == cleanText
qtbot.wait(stepDelay)
# Other Checks # Other Checks
@@ -368,21 +330,17 @@ def testGuiMenu_EditFormat(qtbot, monkeypatch, nwGUI, nwLipsum):
"Also text with \"double\" quotes which are \"less tricky\".\n\n" "Also text with \"double\" quotes which are \"less tricky\".\n\n"
) )
# qtbot.stopForInteraction() # qtbot.stop()
# END Test testGuiMenu_EditFormat # END Test testGuiMenu_EditFormat
@pytest.mark.gui @pytest.mark.gui
def testGuiMenu_ContextMenus(qtbot, monkeypatch, nwGUI, nwLipsum): def testGuiMenu_ContextMenus(qtbot, nwGUI, nwLipsum):
"""Test the context menus. """Test the context menus.
""" """
# Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
assert nwGUI.openProject(nwLipsum) assert nwGUI.openProject(nwLipsum)
assert nwGUI.openDocument("4c4f28287af27") assert nwGUI.openDocument("4c4f28287af27")
qtbot.wait(stepDelay)
# Editor Context Menu # Editor Context Menu
theCursor = nwGUI.docEditor.textCursor() theCursor = nwGUI.docEditor.textCursor()
@@ -452,7 +410,7 @@ def testGuiMenu_ContextMenus(qtbot, monkeypatch, nwGUI, nwLipsum):
assert nwGUI.docViewer.docHeader.backButton.isEnabled() assert nwGUI.docViewer.docHeader.backButton.isEnabled()
assert not nwGUI.docViewer.docHeader.forwardButton.isEnabled() assert not nwGUI.docViewer.docHeader.forwardButton.isEnabled()
# qtbot.stopForInteraction() # qtbot.stop()
# END Test testGuiMenu_ContextMenus # END Test testGuiMenu_ContextMenus
@@ -461,10 +419,6 @@ def testGuiMenu_ContextMenus(qtbot, monkeypatch, nwGUI, nwLipsum):
def testGuiMenu_Insert(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mockRnd): def testGuiMenu_Insert(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mockRnd):
"""Test the Insert menu. """Test the Insert menu.
""" """
# Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
buildTestProject(nwGUI, fncProj) buildTestProject(nwGUI, fncProj)
assert nwGUI.projView.projTree._getTreeItem(C.hSceneDoc) is not None assert nwGUI.projView.projTree._getTreeItem(C.hSceneDoc) is not None
@@ -482,7 +436,7 @@ def testGuiMenu_Insert(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mockRnd):
assert nwGUI.docEditor.insertText(None) is False assert nwGUI.docEditor.insertText(None) is False
assert nwGUI.docEditor.isEmpty() assert nwGUI.docEditor.isEmpty()
# qtbot.stopForInteraction() # qtbot.stop()
# Check Menu Entries # Check Menu Entries
nwGUI.mainMenu.aInsENDash.activate(QAction.Trigger) nwGUI.mainMenu.aInsENDash.activate(QAction.Trigger)
@@ -676,12 +630,12 @@ def testGuiMenu_Insert(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mockRnd):
assert nwGUI.docEditor.getText() == "Bar" assert nwGUI.docEditor.getText() == "Bar"
# The document isn't empty, so the message box should pop # The document isn't empty, so the message box should pop
monkeypatch.setattr(QMessageBox, "question", lambda *a, **k: QMessageBox.No) with monkeypatch.context() as mp:
assert not nwGUI.importDocument() mp.setattr(QMessageBox, "question", lambda *a, **k: QMessageBox.No)
assert nwGUI.docEditor.getText() == "Bar" assert not nwGUI.importDocument()
assert nwGUI.docEditor.getText() == "Bar"
# Finally, accept the replaced text, this time we use the menu entry to trigger it # Finally, accept the replaced text, this time we use the menu entry to trigger it
monkeypatch.setattr(QMessageBox, "question", lambda *a, **k: QMessageBox.Yes)
nwGUI.mainMenu.aImportFile.activate(QAction.Trigger) nwGUI.mainMenu.aImportFile.activate(QAction.Trigger)
assert nwGUI.docEditor.getText() == "Foo" assert nwGUI.docEditor.getText() == "Foo"
@@ -705,6 +659,6 @@ def testGuiMenu_Insert(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mockRnd):
assert theBits[0] == "The currently open file is saved in:" assert theBits[0] == "The currently open file is saved in:"
assert theBits[1] == os.path.join(fncProj, "content", "000000000000f.nwd") assert theBits[1] == os.path.join(fncProj, "content", "000000000000f.nwd")
# qtbot.stopForInteraction() # qtbot.stop()
# END Test testGuiMenu_Insert # END Test testGuiMenu_Insert
+1 -4
View File
@@ -26,7 +26,7 @@ from tools import C, buildTestProject, writeFile
from PyQt5.QtGui import QFocusEvent from PyQt5.QtGui import QFocusEvent
from PyQt5.QtCore import Qt, QEvent from PyQt5.QtCore import Qt, QEvent
from PyQt5.QtWidgets import QMessageBox, QToolTip from PyQt5.QtWidgets import QToolTip
from novelwriter.enum import nwWidget, nwItemType from novelwriter.enum import nwWidget, nwItemType
from novelwriter.gui.noveltree import NovelTreeColumn from novelwriter.gui.noveltree import NovelTreeColumn
@@ -37,9 +37,6 @@ from novelwriter.dialogs.editlabel import GuiEditLabel
def testGuiNovelTree_TreeItems(qtbot, monkeypatch, nwGUI, fncProj, mockRnd): def testGuiNovelTree_TreeItems(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
"""Test navigating the novel tree. """Test navigating the novel tree.
""" """
# Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True)) monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
buildTestProject(nwGUI, fncProj) buildTestProject(nwGUI, fncProj)
+2 -10
View File
@@ -26,7 +26,7 @@ import pytest
from tools import buildTestProject, writeFile from tools import buildTestProject, writeFile
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QWidget, QMessageBox, QAction from PyQt5.QtWidgets import QWidget, QAction
from novelwriter.enum import nwItemClass, nwOutline, nwView from novelwriter.enum import nwItemClass, nwOutline, nwView
@@ -35,10 +35,6 @@ from novelwriter.enum import nwItemClass, nwOutline, nwView
def testGuiOutline_Main(qtbot, monkeypatch, nwGUI, fncDir): def testGuiOutline_Main(qtbot, monkeypatch, nwGUI, fncDir):
"""Test the outline view. """Test the outline view.
""" """
# Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
# Create a project # Create a project
prjDir = os.path.join(fncDir, "project") prjDir = os.path.join(fncDir, "project")
buildTestProject(nwGUI, prjDir) buildTestProject(nwGUI, prjDir)
@@ -156,13 +152,9 @@ def testGuiOutline_Main(qtbot, monkeypatch, nwGUI, fncDir):
@pytest.mark.gui @pytest.mark.gui
def testGuiOutline_Content(qtbot, monkeypatch, nwGUI, nwLipsum): def testGuiOutline_Content(qtbot, nwGUI, nwLipsum):
"""Test the outline view. """Test the outline view.
""" """
# Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
assert nwGUI.openProject(nwLipsum) assert nwGUI.openProject(nwLipsum)
nwGUI.mainConf.lastPath = nwLipsum nwGUI.mainConf.lastPath = nwLipsum
+1 -54
View File
@@ -40,11 +40,6 @@ from novelwriter.dialogs.editlabel import GuiEditLabel
def testGuiProjTree_NewItems(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockRnd): def testGuiProjTree_NewItems(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockRnd):
"""Test adding and removing items from the project tree. """Test adding and removing items from the project tree.
""" """
# Block message box
monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True)) monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
projView = nwGUI.projView projView = nwGUI.projView
@@ -177,11 +172,6 @@ def testGuiProjTree_NewItems(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockRnd)
def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, fncDir, mockRnd): def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, fncDir, mockRnd):
"""Test adding and removing items from the project tree. """Test adding and removing items from the project tree.
""" """
# Block message box
monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True)) monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
projView = nwGUI.projView projView = nwGUI.projView
@@ -283,7 +273,7 @@ def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, fncDir, mockRnd):
assert nwGUI.theProject.tree._treeOrder.index(C.hNovelRoot) == 0 assert nwGUI.theProject.tree._treeOrder.index(C.hNovelRoot) == 0
# Clean up # Clean up
# qtbot.stopForInteraction() # qtbot.stop()
nwGUI.closeProject() nwGUI.closeProject()
# END Test testGuiProjTree_MoveItems # END Test testGuiProjTree_MoveItems
@@ -293,11 +283,6 @@ def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, fncDir, mockRnd):
def testGuiProjTree_RequestDeleteItem(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockRnd): def testGuiProjTree_RequestDeleteItem(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockRnd):
"""Test external requests for removing items from project tree. """Test external requests for removing items from project tree.
""" """
# Block message box
monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True)) monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
projView = nwGUI.projView projView = nwGUI.projView
@@ -382,11 +367,6 @@ def testGuiProjTree_RequestDeleteItem(qtbot, caplog, monkeypatch, nwGUI, fncDir,
def testGuiProjTree_MoveItemToTrash(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockRnd): def testGuiProjTree_MoveItemToTrash(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockRnd):
"""Test moving items to Trash. """Test moving items to Trash.
""" """
# Block message box
monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True)) monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
theProject = nwGUI.theProject theProject = nwGUI.theProject
@@ -441,11 +421,6 @@ def testGuiProjTree_MoveItemToTrash(qtbot, caplog, monkeypatch, nwGUI, fncDir, m
def testGuiProjTree_PermanentlyDeleteItem(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockRnd): def testGuiProjTree_PermanentlyDeleteItem(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockRnd):
"""Test permanently deleting items. """Test permanently deleting items.
""" """
# Block message box
monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True)) monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
theProject = nwGUI.theProject theProject = nwGUI.theProject
@@ -499,11 +474,6 @@ def testGuiProjTree_PermanentlyDeleteItem(qtbot, caplog, monkeypatch, nwGUI, fnc
def testGuiProjTree_EmptyTrash(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockRnd): def testGuiProjTree_EmptyTrash(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockRnd):
"""Test emptying Trash. """Test emptying Trash.
""" """
# Block message box
monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True)) monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
theProject = nwGUI.theProject theProject = nwGUI.theProject
@@ -559,11 +529,6 @@ def testGuiProjTree_ContextMenu(qtbot, monkeypatch, nwGUI, fncDir, mockRnd):
"""Test the building of the project tree context menu. All this does """Test the building of the project tree context menu. All this does
is test that the menu builds. It doesn't open the actual menu, is test that the menu builds. It doesn't open the actual menu,
""" """
# Block message box
monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True)) monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
monkeypatch.setattr(QMenu, "exec_", lambda *a: None) monkeypatch.setattr(QMenu, "exec_", lambda *a: None)
@@ -684,12 +649,6 @@ def testGuiProjTree_MergeDocuments(qtbot, monkeypatch, nwGUI, fncDir, mockRnd, i
""" """
mergeData = {} mergeData = {}
# Block message box
monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(GuiDocMerge, "__init__", lambda *a: None) monkeypatch.setattr(GuiDocMerge, "__init__", lambda *a: None)
monkeypatch.setattr(GuiDocMerge, "exec_", lambda *a: None) monkeypatch.setattr(GuiDocMerge, "exec_", lambda *a: None)
monkeypatch.setattr(GuiDocMerge, "result", lambda *a: QDialog.Accepted) monkeypatch.setattr(GuiDocMerge, "result", lambda *a: QDialog.Accepted)
@@ -794,12 +753,6 @@ def testGuiProjTree_SplitDocument(qtbot, monkeypatch, nwGUI, fncDir, mockRnd, ip
splitData = {} splitData = {}
splitText = [] splitText = []
# Block message box
monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(GuiDocSplit, "__init__", lambda *a: None) monkeypatch.setattr(GuiDocSplit, "__init__", lambda *a: None)
monkeypatch.setattr(GuiDocSplit, "exec_", lambda *a: None) monkeypatch.setattr(GuiDocSplit, "exec_", lambda *a: None)
monkeypatch.setattr(GuiDocSplit, "result", lambda *a: QDialog.Accepted) monkeypatch.setattr(GuiDocSplit, "result", lambda *a: QDialog.Accepted)
@@ -907,12 +860,6 @@ def testGuiProjTree_Other(qtbot, monkeypatch, nwGUI, fncDir, mockRnd):
"""Test various parts of the project tree class not covered by """Test various parts of the project tree class not covered by
other tests. other tests.
""" """
# Block message box
monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
# Create a project # Create a project
prjDir = os.path.join(fncDir, "project") prjDir = os.path.join(fncDir, "project")
buildTestProject(nwGUI, prjDir) buildTestProject(nwGUI, prjDir)
+1 -5
View File
@@ -24,18 +24,14 @@ import pytest
from tools import C, buildTestProject from tools import C, buildTestProject
from PyQt5.QtWidgets import QMessageBox
from novelwriter.enum import nwState from novelwriter.enum import nwState
from novelwriter.core.document import NWDoc from novelwriter.core.document import NWDoc
@pytest.mark.gui @pytest.mark.gui
def testGuiStatusBar_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd): def testGuiStatusBar_Main(qtbot, nwGUI, fncProj, mockRnd):
"""Test the the various features of the status bar. """Test the the various features of the status bar.
""" """
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
buildTestProject(nwGUI, fncProj) buildTestProject(nwGUI, fncProj)
cHandle = nwGUI.theProject.newFile("A Note", C.hCharRoot) cHandle = nwGUI.theProject.newFile("A Note", C.hCharRoot)
newDoc = NWDoc(nwGUI.theProject, cHandle) newDoc = NWDoc(nwGUI.theProject, cHandle)
+400 -133
View File
@@ -19,161 +19,428 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
import os
import shutil
import pytest import pytest
import novelwriter
from PyQt5.QtGui import QColor, QPixmap, QIcon from configparser import ConfigParser
from PyQt5.QtWidgets import QMessageBox
keyDelay = 2 from mock import causeOSError
typeDelay = 1 from novelwriter.constants import nwLabels
stepDelay = 20 from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType
from tools import writeFile
from PyQt5.QtGui import QIcon, QPalette, QPixmap
from PyQt5.QtWidgets import QApplication
from novelwriter.config import Config
from novelwriter.gui.theme import GuiIcons, GuiTheme
@pytest.mark.gui @pytest.mark.gui
def testGuiTheme_Main(qtbot, monkeypatch, nwMinimal, tmpDir): def testGuiTheme_Main(qtbot, nwGUI, fncDir):
"""Test the theme and icon classes. """Test the theme class init.
""" """
# Block message box mainTheme: GuiTheme = nwGUI.mainTheme
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes) mainConf: Config = nwGUI.mainConf
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes)
nwGUI = novelwriter.main( # Methods
["--testmode", "--config=%s" % nwMinimal, "--data=%s" % tmpDir, nwMinimal] # =======
)
qtbot.addWidget(nwGUI)
nwGUI.show()
qtbot.wait(stepDelay)
# Change Settings mSize = mainTheme.getTextWidth("m")
assert novelwriter.CONFIG.confPath == nwMinimal assert mSize > 0
novelwriter.CONFIG.guiTheme = "default_dark" assert mainTheme.getTextWidth("m", mainTheme.guiFont) == mSize
novelwriter.CONFIG.guiSyntax = "tomorrow_night_eighties"
novelwriter.CONFIG.guiIcons = "typicons_colour_dark"
novelwriter.CONFIG.guiFont = "Cantarell"
novelwriter.CONFIG.guiFontSize = 11
novelwriter.CONFIG.confChanged = True
assert novelwriter.CONFIG.saveConfig()
nwGUI.closeMain() # Init Fonts
nwGUI.close() # ==========
del nwGUI
# Re-open # The defaults should be set
assert novelwriter.CONFIG.confPath == nwMinimal defaultFont = mainConf.guiFont
nwGUI = novelwriter.main( defaultSize = mainConf.guiFontSize
["--testmode", "--config=%s" % nwMinimal, "--data=%s" % tmpDir, nwMinimal]
)
assert nwGUI.mainConf.confPath == nwMinimal
qtbot.addWidget(nwGUI)
nwGUI.show()
qtbot.wait(stepDelay)
assert novelwriter.CONFIG.guiTheme == "default_dark" # CHange them to nonsense values
assert novelwriter.CONFIG.guiSyntax == "tomorrow_night_eighties" mainConf.guiFont = "notafont"
assert novelwriter.CONFIG.guiIcons == "typicons_dark" mainConf.guiFontSize = 99
assert novelwriter.CONFIG.guiFont != ""
assert novelwriter.CONFIG.guiFontSize > 0
# Check GUI Colours # Let the theme class set them back to default
thePalette = nwGUI.palette() mainTheme._setGuiFont()
assert thePalette.window().color() == QColor(54, 54, 54) assert mainConf.guiFont == defaultFont
assert thePalette.windowText().color() == QColor(174, 174, 174) assert mainConf.guiFontSize == defaultSize
assert thePalette.base().color() == QColor(62, 62, 62)
assert thePalette.alternateBase().color() == QColor(78, 78, 78)
assert thePalette.text().color() == QColor(174, 174, 174)
assert thePalette.toolTipBase().color() == QColor(255, 255, 192)
assert thePalette.toolTipText().color() == QColor(21, 21, 13)
assert thePalette.button().color() == QColor(62, 62, 62)
assert thePalette.buttonText().color() == QColor(174, 174, 174)
assert thePalette.brightText().color() == QColor(174, 174, 174)
assert thePalette.highlight().color() == QColor(44, 152, 247)
assert thePalette.highlightedText().color() == QColor(255, 255, 255)
assert thePalette.link().color() == QColor(44, 152, 247)
assert thePalette.linkVisited().color() == QColor(44, 152, 247)
assert nwGUI.mainTheme.statNone == [150, 152, 150] # A second call should just restore the defaults again
assert nwGUI.mainTheme.statSaved == [39, 135, 78] mainTheme._setGuiFont()
assert nwGUI.mainTheme.statUnsaved == [138, 32, 32] assert mainConf.guiFont == defaultFont
assert mainConf.guiFontSize == defaultSize
# Check Syntax Colours # Scan for Themes
assert nwGUI.mainTheme.colBack == [45, 45, 45] # ===============
assert nwGUI.mainTheme.colText == [204, 204, 204]
assert nwGUI.mainTheme.colLink == [102, 153, 204]
assert nwGUI.mainTheme.colHead == [102, 153, 204]
assert nwGUI.mainTheme.colHeadH == [102, 153, 204]
assert nwGUI.mainTheme.colEmph == [249, 145, 57]
assert nwGUI.mainTheme.colDialN == [242, 119, 122]
assert nwGUI.mainTheme.colDialD == [153, 204, 153]
assert nwGUI.mainTheme.colDialS == [255, 204, 102]
assert nwGUI.mainTheme.colHidden == [153, 153, 153]
assert nwGUI.mainTheme.colKey == [242, 119, 122]
assert nwGUI.mainTheme.colVal == [204, 153, 204]
assert nwGUI.mainTheme.colSpell == [242, 119, 122]
assert nwGUI.mainTheme.colError == [153, 204, 153]
assert nwGUI.mainTheme.colRepTag == [102, 204, 204]
assert nwGUI.mainTheme.colMod == [249, 145, 57]
# Test Icon class assert mainTheme._listConf({}, "not_a_path") is False
iconCache = nwGUI.mainTheme.iconCache
novelwriter.CONFIG.guiIcons = "invalid"
assert iconCache.updateTheme() is True
assert novelwriter.CONFIG.guiIcons == "typicons_light"
# Ask for a non-existent key themeOne = os.path.join(fncDir, "themes", "themeone.conf")
anImg = iconCache.loadDecoration("nonsense", 20, 20) themeTwo = os.path.join(fncDir, "themes", "themetwo.conf")
assert isinstance(anImg, QPixmap) writeFile(themeOne, "# Stuff")
assert anImg.isNull() writeFile(themeTwo, "# Stuff")
# Add a non-existent file and request it result = {}
iconCache.IMAGE_MAP["nonsense"] = "nofile.jpg" assert mainTheme._listConf(result, os.path.join(fncDir, "themes")) is True
anImg = iconCache.loadDecoration("nonsense", 20, 20) assert result["themeone"] == themeOne
assert isinstance(anImg, QPixmap) assert result["themetwo"] == themeTwo
assert anImg.isNull()
# Get a real image, with different size parameters # Parse Colours
anImg = iconCache.loadDecoration("wiz-back", 20, None) # =============
assert isinstance(anImg, QPixmap)
assert not anImg.isNull()
assert anImg.width() == 20
assert anImg.height() >= 56
anImg = iconCache.loadDecoration("wiz-back", None, 70) parser = ConfigParser()
assert isinstance(anImg, QPixmap) parser["Palette"] = {
assert not anImg.isNull() "colour1": "100, 150, 200",
assert anImg.height() == 70 "colour2": "100, 150, 200, 250",
assert anImg.width() >= 24 "colour3": "250, 250",
"colour4": "-10, 127, 300",
}
anImg = iconCache.loadDecoration("wiz-back", 30, 70) # Test the parser for several valid and invalid values
assert isinstance(anImg, QPixmap) assert mainTheme._parseColour(parser, "Palette", "colour1") == [100, 150, 200]
assert not anImg.isNull() assert mainTheme._parseColour(parser, "Palette", "colour2") == [100, 150, 200]
assert anImg.height() == 70 assert mainTheme._parseColour(parser, "Palette", "colour3") == [0, 0, 0]
assert anImg.width() == 30 assert mainTheme._parseColour(parser, "Palette", "colour4") == [0, 127, 255]
assert mainTheme._parseColour(parser, "Palette", "colour5") == [0, 0, 0]
anImg = iconCache.loadDecoration("wiz-back", None, None) # The palette should load with the parsed values
assert isinstance(anImg, QPixmap) mainTheme._setPalette(parser, "Palette", "colour1", QPalette.Window)
assert not anImg.isNull() assert mainTheme._guiPalette.color(QPalette.Window).getRgb() == (100, 150, 200, 255)
assert anImg.height() >= 1500 mainTheme._setPalette(parser, "Palette", "colour2", QPalette.Window)
assert anImg.width() >= 500 assert mainTheme._guiPalette.color(QPalette.Window).getRgb() == (100, 150, 200, 255)
mainTheme._setPalette(parser, "Palette", "colour3", QPalette.Window)
assert mainTheme._guiPalette.color(QPalette.Window).getRgb() == (0, 0, 0, 255)
mainTheme._setPalette(parser, "Palette", "colour4", QPalette.Window)
assert mainTheme._guiPalette.color(QPalette.Window).getRgb() == (0, 127, 255, 255)
mainTheme._setPalette(parser, "Palette", "colour5", QPalette.Window)
assert mainTheme._guiPalette.color(QPalette.Window).getRgb() == (0, 0, 0, 255)
# Load icons # qtbot.stop()
anIcon = iconCache.getIcon("nonsense")
assert isinstance(anIcon, QIcon)
assert anIcon.isNull()
anIcon = iconCache.getIcon("novelwriter")
assert isinstance(anIcon, QIcon)
assert not anIcon.isNull()
# Check return empty icon if file not found
iconCache.ICON_KEYS.add("testicon3")
anIcon = iconCache.getIcon("testicon3")
assert isinstance(anIcon, QIcon)
assert anIcon.isNull()
# qtbot.stopForInteraction()
nwGUI.closeMain()
nwGUI.close()
# END Test testGuiTheme_Main # END Test testGuiTheme_Main
@pytest.mark.gui
def testGuiTheme_Theme(qtbot, monkeypatch, nwGUI, fncDir):
"""Test the theme part of the class.
"""
mainTheme: GuiTheme = nwGUI.mainTheme
mainConf: Config = nwGUI.mainConf
# List Themes
# ===========
shutil.copy(
os.path.join(mainConf.assetPath, "themes", "default_dark.conf"),
os.path.join(fncDir, "themes")
)
shutil.copy(
os.path.join(mainConf.assetPath, "themes", "default.conf"),
os.path.join(fncDir, "themes")
)
writeFile(os.path.join(fncDir, "themes", "default.qss"), "/* Stuff */")
# Block the reading of the files
with monkeypatch.context() as mp:
mp.setattr("builtins.open", causeOSError)
assert mainTheme.listThemes() == []
# Load the theme info
themesList = mainTheme.listThemes()
assert themesList[0] == ("default_dark", "Default Dark Theme")
assert themesList[1] == ("default", "Default Theme")
# A second call should returned the cached list
assert mainTheme.listThemes() == mainTheme._themeList
# Check handling of broken theme settings
mainConf.guiTheme = "not_a_theme"
assert mainTheme.loadTheme() is False
# Check handling of unreadable file
mainConf.guiTheme = "default"
with monkeypatch.context() as mp:
mp.setattr("builtins.open", causeOSError)
assert mainTheme.loadTheme() is False
# Load Default Theme
# ==================
# Set a mock colour for the window background
mainTheme._guiPalette.color(QPalette.Window).setRgb(0, 0, 0, 0)
# Load the default theme
mainConf.guiTheme = "default"
assert mainTheme.loadTheme() is True
# This should load a standard palette
wCol = QApplication.style().standardPalette().color(QPalette.Window).getRgb()
assert mainTheme._guiPalette.color(QPalette.Window).getRgb() == wCol
# Load Default Dark Theme
# =======================
mainConf.guiTheme = "default_dark"
assert mainTheme.loadTheme() is True
# Check a few values
assert mainTheme._guiPalette.color(QPalette.Window).getRgb() == (54, 54, 54, 255)
assert mainTheme._guiPalette.color(QPalette.WindowText).getRgb() == (174, 174, 174, 255)
assert mainTheme._guiPalette.color(QPalette.Base).getRgb() == (62, 62, 62, 255)
assert mainTheme._guiPalette.color(QPalette.AlternateBase).getRgb() == (78, 78, 78, 255)
# qtbot.stop()
# END Test testGuiTheme_Theme
@pytest.mark.gui
def testGuiTheme_Syntax(qtbot, monkeypatch, nwGUI, fncDir):
"""Test the syntax part of the class.
"""
mainTheme: GuiTheme = nwGUI.mainTheme
mainConf: Config = nwGUI.mainConf
# List Themes
# ===========
shutil.copy(
os.path.join(mainConf.assetPath, "syntax", "default_dark.conf"),
os.path.join(fncDir, "syntax")
)
shutil.copy(
os.path.join(mainConf.assetPath, "syntax", "default_light.conf"),
os.path.join(fncDir, "syntax")
)
# Block the reading of the files
with monkeypatch.context() as mp:
mp.setattr("builtins.open", causeOSError)
assert mainTheme.listThemes() == []
# Load the syntax info
syntaxList = mainTheme.listSyntax()
assert syntaxList[0] == ("default_dark", "Default Dark")
assert syntaxList[1] == ("default_light", "Default Light")
# A second call should returned the cached list
assert mainTheme.listSyntax() == mainTheme._syntaxList
# Check handling of broken theme settings
mainConf.guiSyntax = "not_a_syntax"
assert mainTheme.loadSyntax() is False
# Check handling of unreadable file
mainConf.guiSyntax = "default_light"
with monkeypatch.context() as mp:
mp.setattr("builtins.open", causeOSError)
assert mainTheme.loadSyntax() is False
# Load Default Light Syntax
# =========================
# Load the default syntax
mainConf.guiSyntax = "default_light"
assert mainTheme.loadSyntax() is True
# Check some values
assert mainTheme.syntaxName == "Default Light"
assert mainTheme.colBack == [255, 255, 255]
assert mainTheme.colText == [0, 0, 0]
assert mainTheme.colLink == [0, 0, 200]
# Load Default Dark Theme
# =======================
# Load the default syntax
mainConf.guiSyntax = "default_dark"
assert mainTheme.loadSyntax() is True
# Check some values
assert mainTheme.syntaxName == "Default Dark"
assert mainTheme.colBack == [54, 54, 54]
assert mainTheme.colText == [199, 207, 208]
assert mainTheme.colLink == [184, 200, 0]
# qtbot.stop()
# END Test testGuiTheme_Syntax
@pytest.mark.gui
def testGuiTheme_Icons(qtbot, caplog, monkeypatch, nwGUI, fncDir):
"""Test the icon cache class.
"""
iconCache: GuiIcons = nwGUI.mainTheme.iconCache
mainConf: Config = nwGUI.mainConf
# Load Theme
# ==========
# Invalid theme name
assert iconCache.loadTheme("not_a_theme") is False
# Check handling of unreadable file
with monkeypatch.context() as mp:
mp.setattr("builtins.open", causeOSError)
assert iconCache.loadTheme("typicons_dark") is False
# Load a broken theme file
iconsDir = os.path.join(fncDir, "icons")
os.mkdir(iconsDir)
os.mkdir(os.path.join(iconsDir, "testicons"))
writeFile(os.path.join(iconsDir, "testicons", "icons.conf"), (
"[Main]\n"
"name = Test Icons\n"
"\n"
"[Map]\n"
"add = add.svg\n"
"stuff = stuff.svg\n"
))
assetPath = mainConf.assetPath
mainConf.assetPath = fncDir
caplog.clear()
assert iconCache.loadTheme("testicons") is True
assert "Unknown icon name 'stuff' in config file" in caplog.text
assert "Icon file 'add.svg' not in theme folder" in caplog.text
mainConf.assetPath = assetPath
# Load working theme file
assert iconCache.loadTheme("typicons_dark") is True
assert "add" in iconCache._themeMap
# Load Decorations
# ================
# Invalid name should return empty pixmap
qPix = iconCache.loadDecoration("stuff")
assert qPix.isNull() is True
# Load an image
qPix = iconCache.loadDecoration("wiz-back")
assert qPix.isNull() is False
# Fail finding the file
with monkeypatch.context() as mp:
mp.setattr("os.path.isfile", lambda *a: False)
qPix = iconCache.loadDecoration("wiz-back")
assert qPix.isNull() is True
# Test image sizes
qPix = iconCache.loadDecoration("wiz-back", pxW=100, pxH=None)
assert qPix.isNull() is False
assert qPix.width() == 100
assert qPix.height() > 100
qPix = iconCache.loadDecoration("wiz-back", pxW=None, pxH=100)
assert qPix.isNull() is False
assert qPix.width() < 100
assert qPix.height() == 100
qPix = iconCache.loadDecoration("wiz-back", pxW=100, pxH=100)
assert qPix.isNull() is False
assert qPix.width() == 100
assert qPix.height() == 100
# Load Icons
# ==========
# Load an unknown icon
qIcon = iconCache.getIcon("stuff")
assert isinstance(qIcon, QIcon)
assert qIcon.isNull() is True
# Load an icon, it is likelyu already cached
qIcon = iconCache.getIcon("add")
assert isinstance(qIcon, QIcon)
assert qIcon.isNull() is False
# Load it as a pixmap with a size
qPix = iconCache.getPixmap("add", (50, 50))
assert isinstance(qPix, QPixmap)
assert qPix.isNull() is False
assert qPix.width() == 50
assert qPix.height() == 50
# Load app icon
qIcon = iconCache.getIcon("novelwriter")
assert isinstance(qIcon, QIcon)
assert qIcon.isNull() is False
# Load mime icon
qIcon = iconCache.getIcon("proj_nwx")
assert isinstance(qIcon, QIcon)
assert qIcon.isNull() is False
# Load Item Icons
# ===============
# Root -> Not Null
assert iconCache.getItemIcon(
nwItemType.ROOT, nwItemClass.NOVEL, nwItemLayout.NO_LAYOUT, hLevel="H0"
) == iconCache.getIcon(nwLabels.CLASS_ICON[nwItemClass.NOVEL])
# Folder -> Not Null
assert iconCache.getItemIcon(
nwItemType.FOLDER, nwItemClass.NOVEL, nwItemLayout.NO_LAYOUT, hLevel="H0"
) == iconCache.getIcon("proj_folder")
# Document H0 -> Not Null
assert iconCache.getItemIcon(
nwItemType.FILE, nwItemClass.NOVEL, nwItemLayout.NO_LAYOUT, hLevel="H0"
) == iconCache.getIcon("proj_document")
# Document H1 -> Not Null
assert iconCache.getItemIcon(
nwItemType.FILE, nwItemClass.NOVEL, nwItemLayout.DOCUMENT, hLevel="H1"
) == iconCache.getIcon("proj_title")
# Document H2 -> Not Null
assert iconCache.getItemIcon(
nwItemType.FILE, nwItemClass.NOVEL, nwItemLayout.DOCUMENT, hLevel="H2"
) == iconCache.getIcon("proj_chapter")
# Document H3 -> Not Null
assert iconCache.getItemIcon(
nwItemType.FILE, nwItemClass.NOVEL, nwItemLayout.DOCUMENT, hLevel="H3"
) == iconCache.getIcon("proj_scene")
# Document H4 -> Not Null
assert iconCache.getItemIcon(
nwItemType.FILE, nwItemClass.NOVEL, nwItemLayout.DOCUMENT, hLevel="H4"
) == iconCache.getIcon("proj_section")
# Document H5 -> Not Null
assert iconCache.getItemIcon(
nwItemType.FILE, nwItemClass.NOVEL, nwItemLayout.NO_LAYOUT, hLevel="H4"
) == iconCache.getIcon("proj_document")
# Note -> Not Null
assert iconCache.getItemIcon(
nwItemType.FILE, nwItemClass.NOVEL, nwItemLayout.NOTE, hLevel="H5"
) == iconCache.getIcon("proj_note")
# No Type -> Null
assert iconCache.getItemIcon(
nwItemType.NO_TYPE, nwItemClass.NOVEL, nwItemLayout.DOCUMENT, hLevel="H0"
).isNull() is True
# Header Decorations
# ==================
assert iconCache.getHeaderDecoration(-1) == iconCache._headerDec[0]
assert iconCache.getHeaderDecoration(0) == iconCache._headerDec[0]
assert iconCache.getHeaderDecoration(1) == iconCache._headerDec[1]
assert iconCache.getHeaderDecoration(2) == iconCache._headerDec[2]
assert iconCache.getHeaderDecoration(3) == iconCache._headerDec[3]
assert iconCache.getHeaderDecoration(4) == iconCache._headerDec[4]
assert iconCache.getHeaderDecoration(5) == iconCache._headerDec[4]
# qtbot.stop()
# END Test testGuiTheme_Icons
+2 -26
View File
@@ -26,22 +26,16 @@ from shutil import copyfile
from tools import cmpFiles, getGuiItem from tools import cmpFiles, getGuiItem
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QAction, QMessageBox, QFileDialog from PyQt5.QtWidgets import QAction, QFileDialog
from novelwriter.tools import GuiBuildNovel from novelwriter.tools import GuiBuildNovel
keyDelay = 2
typeDelay = 1
stepDelay = 20
@pytest.mark.gui @pytest.mark.gui
def testToolBuild_Main(qtbot, monkeypatch, nwGUI, nwLipsum, refDir, outDir): def testToolBuild_Main(qtbot, monkeypatch, nwGUI, nwLipsum, refDir, outDir):
"""Test the build tool. """Test the build tool.
""" """
# Block message box # Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QFileDialog, "getSaveFileName", lambda a, b, c, **k: (c, None)) monkeypatch.setattr(QFileDialog, "getSaveFileName", lambda a, b, c, **k: (c, None))
# Check that we cannot open when there is no project # Check that we cannot open when there is no project
@@ -121,27 +115,17 @@ def testToolBuild_Main(qtbot, monkeypatch, nwGUI, nwLipsum, refDir, outDir):
# Change Title Formats and Flip Switches # Change Title Formats and Flip Switches
nwBuild.fmtChapter.setText(r"Chapter %chw%: %title%") nwBuild.fmtChapter.setText(r"Chapter %chw%: %title%")
qtbot.wait(stepDelay)
nwBuild.fmtScene.setText(r"Scene %ch%.%sc%: %title%") nwBuild.fmtScene.setText(r"Scene %ch%.%sc%: %title%")
qtbot.wait(stepDelay)
nwBuild.fmtSection.setText(r"%ch%.%sc%.1: %title%") nwBuild.fmtSection.setText(r"%ch%.%sc%.1: %title%")
qtbot.wait(stepDelay)
qtbot.mouseClick(nwBuild.justifyText, Qt.LeftButton) qtbot.mouseClick(nwBuild.justifyText, Qt.LeftButton)
qtbot.wait(stepDelay)
qtbot.mouseClick(nwBuild.includeSynopsis, Qt.LeftButton) qtbot.mouseClick(nwBuild.includeSynopsis, Qt.LeftButton)
qtbot.wait(stepDelay)
qtbot.mouseClick(nwBuild.includeComments, Qt.LeftButton) qtbot.mouseClick(nwBuild.includeComments, Qt.LeftButton)
qtbot.wait(stepDelay)
qtbot.mouseClick(nwBuild.includeKeywords, Qt.LeftButton) qtbot.mouseClick(nwBuild.includeKeywords, Qt.LeftButton)
qtbot.wait(stepDelay)
qtbot.mouseClick(nwBuild.replaceUCode, Qt.LeftButton) qtbot.mouseClick(nwBuild.replaceUCode, Qt.LeftButton)
qtbot.wait(stepDelay)
qtbot.mouseClick(nwBuild.noteFiles, Qt.LeftButton) qtbot.mouseClick(nwBuild.noteFiles, Qt.LeftButton)
qtbot.wait(stepDelay)
qtbot.mouseClick(nwBuild.ignoreFlag, Qt.LeftButton) qtbot.mouseClick(nwBuild.ignoreFlag, Qt.LeftButton)
qtbot.wait(stepDelay)
qtbot.mouseClick(nwBuild.buildNovel, Qt.LeftButton) qtbot.mouseClick(nwBuild.buildNovel, Qt.LeftButton)
@@ -175,7 +159,6 @@ def testToolBuild_Main(qtbot, monkeypatch, nwGUI, nwLipsum, refDir, outDir):
# Replace Tabs with Spaces # Replace Tabs with Spaces
qtbot.mouseClick(nwBuild.replaceTabs, Qt.LeftButton) qtbot.mouseClick(nwBuild.replaceTabs, Qt.LeftButton)
qtbot.wait(stepDelay)
qtbot.mouseClick(nwBuild.buildNovel, Qt.LeftButton) qtbot.mouseClick(nwBuild.buildNovel, Qt.LeftButton)
@@ -210,20 +193,13 @@ def testToolBuild_Main(qtbot, monkeypatch, nwGUI, nwLipsum, refDir, outDir):
# Putline Mode # Putline Mode
nwBuild.fmtChapter.setText(r"Chapter %chw%: %title%") nwBuild.fmtChapter.setText(r"Chapter %chw%: %title%")
qtbot.wait(stepDelay)
nwBuild.fmtScene.setText(r"Scene %sca%: %title%") nwBuild.fmtScene.setText(r"Scene %sca%: %title%")
qtbot.wait(stepDelay)
nwBuild.fmtSection.setText(r"Section: %title%") nwBuild.fmtSection.setText(r"Section: %title%")
qtbot.wait(stepDelay)
qtbot.mouseClick(nwBuild.includeComments, Qt.LeftButton) qtbot.mouseClick(nwBuild.includeComments, Qt.LeftButton)
qtbot.wait(stepDelay)
qtbot.mouseClick(nwBuild.noteFiles, Qt.LeftButton) qtbot.mouseClick(nwBuild.noteFiles, Qt.LeftButton)
qtbot.wait(stepDelay)
qtbot.mouseClick(nwBuild.ignoreFlag, Qt.LeftButton) qtbot.mouseClick(nwBuild.ignoreFlag, Qt.LeftButton)
qtbot.wait(stepDelay)
qtbot.mouseClick(nwBuild.includeBody, Qt.LeftButton) qtbot.mouseClick(nwBuild.includeBody, Qt.LeftButton)
qtbot.wait(stepDelay)
qtbot.mouseClick(nwBuild.buildNovel, Qt.LeftButton) qtbot.mouseClick(nwBuild.buildNovel, Qt.LeftButton)
@@ -287,6 +263,6 @@ def testToolBuild_Main(qtbot, monkeypatch, nwGUI, nwLipsum, refDir, outDir):
nwBuild._doClose() nwBuild._doClose()
# qtbot.stopForInteraction() # qtbot.stop()
# END Test testToolBuild_Main # END Test testToolBuild_Main
+3 -6
View File
@@ -23,18 +23,15 @@ import pytest
from tools import C, getGuiItem, buildTestProject from tools import C, getGuiItem, buildTestProject
from PyQt5.QtWidgets import QAction, QMessageBox from PyQt5.QtWidgets import QAction
from novelwriter.tools import GuiLipsum from novelwriter.tools import GuiLipsum
@pytest.mark.gui @pytest.mark.gui
def testToolLipsum_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd): def testToolLipsum_Main(qtbot, nwGUI, fncProj, mockRnd):
"""Test the Lorem Ipsum tool. """Test the Lorem Ipsum tool.
""" """
# Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
# Check that we cannot open when there is no project # Check that we cannot open when there is no project
nwGUI.mainMenu.aLipsumText.activate(QAction.Trigger) nwGUI.mainMenu.aLipsumText.activate(QAction.Trigger)
assert getGuiItem("GuiLipsum") is None assert getGuiItem("GuiLipsum") is None
@@ -70,6 +67,6 @@ def testToolLipsum_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
# Close # Close
nwLipsum._doClose() nwLipsum._doClose()
# qtbot.stopForInteraction() # qtbot.stop()
# END Test testToolLipsum_Main # END Test testToolLipsum_Main
+8 -26
View File
@@ -26,7 +26,7 @@ import pytest
from tools import getGuiItem from tools import getGuiItem
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QFileDialog, QWizard, QMessageBox, QDialog from PyQt5.QtWidgets import QFileDialog, QWizard, QDialog
from novelwriter.enum import nwItemClass from novelwriter.enum import nwItemClass
from novelwriter.tools.projwizard import ( from novelwriter.tools.projwizard import (
@@ -34,10 +34,6 @@ from novelwriter.tools.projwizard import (
ProjWizardPopulatePage, ProjWizardCustomPage, ProjWizardFinalPage ProjWizardPopulatePage, ProjWizardCustomPage, ProjWizardFinalPage
) )
keyDelay = 2
typeDelay = 1
stepDelay = 20
@pytest.mark.gui @pytest.mark.gui
@pytest.mark.skipif(sys.platform.startswith("darwin"), reason="Not running on Darwin") @pytest.mark.skipif(sys.platform.startswith("darwin"), reason="Not running on Darwin")
@@ -45,13 +41,8 @@ def testToolProjectWizard_Handling(qtbot, monkeypatch, nwGUI, nwMinimal):
"""Test the launch of the project wizard. """Test the launch of the project wizard.
Disabled for macOS because the test segfaults on QWizard.show() Disabled for macOS because the test segfaults on QWizard.show()
""" """
# Block message box # Test New Project Function
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes) # ========================
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
##
# Test New Project Function
##
# New with a project open should cause an error # New with a project open should cause an error
assert nwGUI.openProject(nwMinimal) assert nwGUI.openProject(nwMinimal)
@@ -73,9 +64,8 @@ def testToolProjectWizard_Handling(qtbot, monkeypatch, nwGUI, nwMinimal):
mp.setattr(nwGUI, "showNewProjectDialog", lambda *a: {"projPath": nwMinimal}) mp.setattr(nwGUI, "showNewProjectDialog", lambda *a: {"projPath": nwMinimal})
assert nwGUI.newProject() is False assert nwGUI.newProject() is False
## # Test the Wizard Launching
# Test the Wizard Launching # =========================
##
nwGUI.mainConf.lastPath = " " nwGUI.mainConf.lastPath = " "
monkeypatch.setattr(GuiProjectWizard, "exec_", lambda *a: None) monkeypatch.setattr(GuiProjectWizard, "exec_", lambda *a: None)
@@ -87,7 +77,6 @@ def testToolProjectWizard_Handling(qtbot, monkeypatch, nwGUI, nwMinimal):
assert isinstance(nwWiz, GuiProjectWizard) assert isinstance(nwWiz, GuiProjectWizard)
nwWiz.show() nwWiz.show()
qtbot.wait(stepDelay)
qtbot.mouseClick(nwWiz.button(QWizard.CancelButton), Qt.LeftButton) qtbot.mouseClick(nwWiz.button(QWizard.CancelButton), Qt.LeftButton)
assert result is None assert result is None
@@ -100,7 +89,7 @@ def testToolProjectWizard_Handling(qtbot, monkeypatch, nwGUI, nwMinimal):
nwWiz.reject() nwWiz.reject()
nwWiz.close() nwWiz.close()
# qtbot.stopForInteraction() # qtbot.stop()
# END Test testToolProjectWizard_Handling # END Test testToolProjectWizard_Handling
@@ -111,14 +100,12 @@ def testToolProjectWizard_Handling(qtbot, monkeypatch, nwGUI, nwMinimal):
def testToolProjectWizard_Run(qtbot, monkeypatch, nwGUI, fncDir, prjType): def testToolProjectWizard_Run(qtbot, monkeypatch, nwGUI, fncDir, prjType):
"""Test the new project wizard with a set of selection scenarios. """Test the new project wizard with a set of selection scenarios.
""" """
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(GuiProjectWizard, "exec_", lambda *a: None) monkeypatch.setattr(GuiProjectWizard, "exec_", lambda *a: None)
nwGUI.mainConf.lastPath = " " nwGUI.mainConf.lastPath = " "
nwWiz = GuiProjectWizard(nwGUI) nwWiz = GuiProjectWizard(nwGUI)
nwWiz.show() nwWiz.show()
qtbot.wait(stepDelay) qtbot.addWidget(nwWiz)
# Intro Page # Intro Page
# ========== # ==========
@@ -134,7 +121,6 @@ def testToolProjectWizard_Run(qtbot, monkeypatch, nwGUI, fncDir, prjType):
# Setting projName should activate the button # Setting projName should activate the button
assert nwWiz.button(QWizard.NextButton).isEnabled() assert nwWiz.button(QWizard.NextButton).isEnabled()
qtbot.wait(stepDelay)
qtbot.mouseClick(nwWiz.button(QWizard.NextButton), Qt.LeftButton) qtbot.mouseClick(nwWiz.button(QWizard.NextButton), Qt.LeftButton)
# Folder Page # Folder Page
@@ -173,7 +159,6 @@ def testToolProjectWizard_Run(qtbot, monkeypatch, nwGUI, fncDir, prjType):
# Setting projPath should activate the button # Setting projPath should activate the button
assert nwWiz.button(QWizard.NextButton).isEnabled() assert nwWiz.button(QWizard.NextButton).isEnabled()
qtbot.wait(stepDelay)
qtbot.mouseClick(nwWiz.button(QWizard.NextButton), Qt.LeftButton) qtbot.mouseClick(nwWiz.button(QWizard.NextButton), Qt.LeftButton)
# Populate Page # Populate Page
@@ -183,7 +168,6 @@ def testToolProjectWizard_Run(qtbot, monkeypatch, nwGUI, fncDir, prjType):
assert isinstance(popPage, ProjWizardPopulatePage) assert isinstance(popPage, ProjWizardPopulatePage)
assert nwWiz.button(QWizard.NextButton).isEnabled() assert nwWiz.button(QWizard.NextButton).isEnabled()
qtbot.wait(stepDelay)
if prjType.startswith("minimal"): if prjType.startswith("minimal"):
popPage.popMinimal.setChecked(True) popPage.popMinimal.setChecked(True)
elif prjType.startswith("custom"): elif prjType.startswith("custom"):
@@ -191,7 +175,6 @@ def testToolProjectWizard_Run(qtbot, monkeypatch, nwGUI, fncDir, prjType):
elif prjType.startswith("sample"): elif prjType.startswith("sample"):
popPage.popSample.setChecked(True) popPage.popSample.setChecked(True)
qtbot.wait(stepDelay)
qtbot.mouseClick(nwWiz.button(QWizard.NextButton), Qt.LeftButton) qtbot.mouseClick(nwWiz.button(QWizard.NextButton), Qt.LeftButton)
# Custom Page # Custom Page
@@ -219,7 +202,6 @@ def testToolProjectWizard_Run(qtbot, monkeypatch, nwGUI, fncDir, prjType):
customPage.numChapters.setValue(0) customPage.numChapters.setValue(0)
customPage.numScenes.setValue(10) customPage.numScenes.setValue(10)
qtbot.wait(stepDelay)
qtbot.mouseClick(nwWiz.button(QWizard.NextButton), Qt.LeftButton) qtbot.mouseClick(nwWiz.button(QWizard.NextButton), Qt.LeftButton)
# Final Page # Final Page
@@ -264,6 +246,6 @@ def testToolProjectWizard_Run(qtbot, monkeypatch, nwGUI, fncDir, prjType):
nwWiz.reject() nwWiz.reject()
nwWiz.close() nwWiz.close()
# qtbot.stopForInteraction() # qtbot.stop()
# END Test testToolProjectWizard_Run # END Test testToolProjectWizard_Run
+3 -25
View File
@@ -27,26 +27,16 @@ from mock import causeOSError
from tools import getGuiItem, writeFile, buildTestProject from tools import getGuiItem, writeFile, buildTestProject
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QAction, QFileDialog, QMessageBox from PyQt5.QtWidgets import QAction, QFileDialog
from novelwriter.tools import GuiWritingStats from novelwriter.tools import GuiWritingStats
from novelwriter.constants import nwFiles from novelwriter.constants import nwFiles
keyDelay = 2
typeDelay = 1
stepDelay = 20
@pytest.mark.gui @pytest.mark.gui
def testToolWritingStats_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj): def testToolWritingStats_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj):
"""Test the full writing stats tool. """Test the full writing stats tool.
""" """
# Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
# Create a project to work on # Create a project to work on
buildTestProject(nwGUI, fncProj) buildTestProject(nwGUI, fncProj)
qtbot.wait(100) qtbot.wait(100)
@@ -60,7 +50,6 @@ def testToolWritingStats_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj):
sessLog = getGuiItem("GuiWritingStats") sessLog = getGuiItem("GuiWritingStats")
assert isinstance(sessLog, GuiWritingStats) assert isinstance(sessLog, GuiWritingStats)
qtbot.wait(stepDelay)
# Test Loading # Test Loading
# ============ # ============
@@ -186,9 +175,7 @@ def testToolWritingStats_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj):
# No Novel Files # No Novel Files
qtbot.mouseClick(sessLog.incNovel, Qt.LeftButton) qtbot.mouseClick(sessLog.incNovel, Qt.LeftButton)
qtbot.wait(stepDelay)
assert sessLog._saveData(sessLog.FMT_JSON) assert sessLog._saveData(sessLog.FMT_JSON)
qtbot.wait(stepDelay)
jsonStats = os.path.join(fncDir, "sessionStats.json") jsonStats = os.path.join(fncDir, "sessionStats.json")
with open(jsonStats, mode="r", encoding="utf-8") as inFile: with open(jsonStats, mode="r", encoding="utf-8") as inFile:
@@ -234,9 +221,7 @@ def testToolWritingStats_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj):
# No Note Files # No Note Files
qtbot.mouseClick(sessLog.incNovel, Qt.LeftButton) qtbot.mouseClick(sessLog.incNovel, Qt.LeftButton)
qtbot.mouseClick(sessLog.incNotes, Qt.LeftButton) qtbot.mouseClick(sessLog.incNotes, Qt.LeftButton)
qtbot.wait(stepDelay)
assert sessLog._saveData(sessLog.FMT_JSON) assert sessLog._saveData(sessLog.FMT_JSON)
qtbot.wait(stepDelay)
jsonStats = os.path.join(fncDir, "sessionStats.json") jsonStats = os.path.join(fncDir, "sessionStats.json")
with open(jsonStats, mode="r", encoding="utf-8") as inFile: with open(jsonStats, mode="r", encoding="utf-8") as inFile:
@@ -282,11 +267,9 @@ def testToolWritingStats_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj):
# No Negative Entries # No Negative Entries
qtbot.mouseClick(sessLog.incNotes, Qt.LeftButton) qtbot.mouseClick(sessLog.incNotes, Qt.LeftButton)
qtbot.mouseClick(sessLog.hideNegative, Qt.LeftButton) qtbot.mouseClick(sessLog.hideNegative, Qt.LeftButton)
qtbot.wait(stepDelay)
assert sessLog._saveData(sessLog.FMT_JSON) assert sessLog._saveData(sessLog.FMT_JSON)
qtbot.wait(stepDelay)
# qtbot.stopForInteraction() # qtbot.stop()
jsonStats = os.path.join(fncDir, "sessionStats.json") jsonStats = os.path.join(fncDir, "sessionStats.json")
with open(jsonStats, mode="r", encoding="utf-8") as inFile: with open(jsonStats, mode="r", encoding="utf-8") as inFile:
@@ -316,9 +299,7 @@ def testToolWritingStats_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj):
# Un-hide Zero Entries # Un-hide Zero Entries
qtbot.mouseClick(sessLog.hideNegative, Qt.LeftButton) qtbot.mouseClick(sessLog.hideNegative, Qt.LeftButton)
qtbot.mouseClick(sessLog.hideZeros, Qt.LeftButton) qtbot.mouseClick(sessLog.hideZeros, Qt.LeftButton)
qtbot.wait(stepDelay)
assert sessLog._saveData(sessLog.FMT_JSON) assert sessLog._saveData(sessLog.FMT_JSON)
qtbot.wait(stepDelay)
jsonStats = os.path.join(fncDir, "sessionStats.json") jsonStats = os.path.join(fncDir, "sessionStats.json")
with open(jsonStats, mode="r", encoding="utf-8") as inFile: with open(jsonStats, mode="r", encoding="utf-8") as inFile:
@@ -371,9 +352,7 @@ def testToolWritingStats_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj):
# Group by Day # Group by Day
qtbot.mouseClick(sessLog.groupByDay, Qt.LeftButton) qtbot.mouseClick(sessLog.groupByDay, Qt.LeftButton)
qtbot.wait(stepDelay)
assert sessLog._saveData(sessLog.FMT_JSON) assert sessLog._saveData(sessLog.FMT_JSON)
qtbot.wait(stepDelay)
jsonStats = os.path.join(fncDir, "sessionStats.json") jsonStats = os.path.join(fncDir, "sessionStats.json")
with open(jsonStats, mode="r", encoding="utf-8") as inFile: with open(jsonStats, mode="r", encoding="utf-8") as inFile:
@@ -422,10 +401,9 @@ def testToolWritingStats_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj):
assert not sessLog._loadLogFile() assert not sessLog._loadLogFile()
assert not sessLog._saveData(sessLog.FMT_CSV) assert not sessLog._saveData(sessLog.FMT_CSV)
# qtbot.stopForInteraction() # qtbot.stop()
sessLog._doClose() sessLog._doClose()
assert nwGUI.closeProject() assert nwGUI.closeProject()
qtbot.wait(stepDelay)
# END Test testToolWritingStats_Main # END Test testToolWritingStats_Main