diff --git a/novelwriter/common.py b/novelwriter/common.py index 0f61bd896..91b2fae97 100644 --- a/novelwriter/common.py +++ b/novelwriter/common.py @@ -38,7 +38,6 @@ from PyQt5.QtGui import QDesktopServices from PyQt5.QtCore import QCoreApplication, QUrl -from PyQt5.QtWidgets import QWidget, qApp from novelwriter.enum import nwItemClass, nwItemType, nwItemLayout from novelwriter.error import logException @@ -508,18 +507,6 @@ def openExternalPath(path: Path) -> bool: return False -# =============================================================================================== # -# Other Functions -# =============================================================================================== # - -def getGuiItem(objName: str) -> QWidget | None: - """Returns a QtWidget based on its objectName.""" - for qWidget in qApp.topLevelWidgets(): - if qWidget.objectName() == objName: - return qWidget - return None - - # =============================================================================================== # # Classes # =============================================================================================== # diff --git a/novelwriter/core/project.py b/novelwriter/core/project.py index f66f4549a..1f74bb430 100644 --- a/novelwriter/core/project.py +++ b/novelwriter/core/project.py @@ -80,7 +80,7 @@ def __init__(self) -> None: return - def __del__(self): # pragma: no cover + def __del__(self) -> None: # pragma: no cover logger.debug("Delete: NWProject") return diff --git a/novelwriter/core/spellcheck.py b/novelwriter/core/spellcheck.py index e2a022e92..852fbd485 100644 --- a/novelwriter/core/spellcheck.py +++ b/novelwriter/core/spellcheck.py @@ -56,7 +56,7 @@ def __init__(self, project: NWProject) -> None: logger.debug("Ready: NWSpellEnchant") return - def __del__(self): # pragma: no cover + def __del__(self) -> None: # pragma: no cover logger.debug("Delete: NWSpellEnchant") return diff --git a/novelwriter/extensions/wheeleventfilter.py b/novelwriter/extensions/eventfilters.py similarity index 80% rename from novelwriter/extensions/wheeleventfilter.py rename to novelwriter/extensions/eventfilters.py index 220fa324b..9c53354e3 100644 --- a/novelwriter/extensions/wheeleventfilter.py +++ b/novelwriter/extensions/eventfilters.py @@ -1,9 +1,10 @@ """ -novelWriter – Custom Object: Wheel Event Filter -=============================================== +novelWriter – Custom Objects: Event Filters +=========================================== File History: -Created: 2023-08-31 [2.1rc1] +Created: 2023-08-31 [2.1rc1] WheelEventFilter +Created: 2023-11-28 [2.2] StatusTipFilter This file is a part of novelWriter Copyright 2018–2023, Veronica Berglyd Olsen @@ -23,7 +24,7 @@ """ from __future__ import annotations -from PyQt5.QtGui import QWheelEvent +from PyQt5.QtGui import QStatusTipEvent, QWheelEvent from PyQt5.QtCore import QEvent, QObject from PyQt5.QtWidgets import QWidget @@ -63,3 +64,12 @@ def eventFilter(self, object: QObject, event: QEvent) -> bool: return False # END Class WheelEventFilter + + +class StatusTipFilter(QObject): + + def eventFilter(self, obj: QObject, event: QEvent) -> bool: + """Filter out status tip events on menus.""" + return True if isinstance(event, QStatusTipEvent) else super().eventFilter(obj, event) + +# END Class StatusTipFilter diff --git a/novelwriter/gui/doceditor.py b/novelwriter/gui/doceditor.py index 4d4ac3930..8ea95769a 100644 --- a/novelwriter/gui/doceditor.py +++ b/novelwriter/gui/doceditor.py @@ -62,7 +62,7 @@ from novelwriter.core.document import NWDocument from novelwriter.gui.dochighlight import GuiDocHighlighter from novelwriter.gui.editordocument import GuiTextDocument -from novelwriter.extensions.wheeleventfilter import WheelEventFilter +from novelwriter.extensions.eventfilters import WheelEventFilter if TYPE_CHECKING: # pragma: no cover from novelwriter.guimain import GuiMain diff --git a/novelwriter/gui/docviewer.py b/novelwriter/gui/docviewer.py index d548f0d41..44ccb1d5c 100644 --- a/novelwriter/gui/docviewer.py +++ b/novelwriter/gui/docviewer.py @@ -46,7 +46,7 @@ from novelwriter.error import logException from novelwriter.constants import nwUnicode from novelwriter.core.tohtml import ToHtml -from novelwriter.extensions.wheeleventfilter import WheelEventFilter +from novelwriter.extensions.eventfilters import WheelEventFilter if TYPE_CHECKING: # pragma: no cover from novelwriter.guimain import GuiMain diff --git a/novelwriter/gui/editordocument.py b/novelwriter/gui/editordocument.py index aa00ec7c1..3f6661e0b 100644 --- a/novelwriter/gui/editordocument.py +++ b/novelwriter/gui/editordocument.py @@ -50,7 +50,7 @@ def __init__(self, parent: QObject) -> None: return - def __del__(self): # pragma: no cover + def __del__(self) -> None: # pragma: no cover logger.debug("Delete: GuiTextDocument") return diff --git a/novelwriter/gui/mainmenu.py b/novelwriter/gui/mainmenu.py index a601fb99c..e442f9e81 100644 --- a/novelwriter/gui/mainmenu.py +++ b/novelwriter/gui/mainmenu.py @@ -3,8 +3,7 @@ =========================== File History: -Created: 2019-04-27 [0.0.1] GuiMainMenu -Created: 2023-11-28 [2.2] StatusTipFilter +Created: 2019-04-27 [0.0.1] This file is a part of novelWriter Copyright 2018–2023, Veronica Berglyd Olsen @@ -29,14 +28,15 @@ from typing import TYPE_CHECKING from pathlib import Path -from PyQt5.QtGui import QDesktopServices, QStatusTipEvent -from PyQt5.QtCore import QEvent, QObject, QUrl, pyqtSignal, pyqtSlot +from PyQt5.QtGui import QDesktopServices +from PyQt5.QtCore import QUrl, pyqtSignal, pyqtSlot from PyQt5.QtWidgets import QMenuBar, QAction from novelwriter import CONFIG, SHARED from novelwriter.enum import nwDocAction, nwDocInsert, nwWidget from novelwriter.common import openExternalPath from novelwriter.constants import nwConst, trConst, nwKeyWords, nwLabels, nwUnicode +from novelwriter.extensions.eventfilters import StatusTipFilter if TYPE_CHECKING: # pragma: no cover from novelwriter.guimain import GuiMain @@ -971,12 +971,3 @@ def _buildHelpMenu(self) -> None: return # END Class GuiMainMenu - - -class StatusTipFilter(QObject): - - def eventFilter(self, obj: QObject, event: QEvent) -> bool: - """Filter out status tip events.""" - return True if isinstance(event, QStatusTipEvent) else super().eventFilter(obj, event) - -# END Class StatusTipFilter diff --git a/novelwriter/gui/projtree.py b/novelwriter/gui/projtree.py index 607e694a7..c2355707f 100644 --- a/novelwriter/gui/projtree.py +++ b/novelwriter/gui/projtree.py @@ -1650,7 +1650,7 @@ def __init__(self, projTree: GuiProjectTree, nwItem: NWItem) -> None: return - def __del__(self): # pragma: no cover + def __del__(self) -> None: # pragma: no cover logger.debug("Delete: _TreeContextMenu") return diff --git a/novelwriter/gui/sidebar.py b/novelwriter/gui/sidebar.py index 9cfbf1181..b15908a24 100644 --- a/novelwriter/gui/sidebar.py +++ b/novelwriter/gui/sidebar.py @@ -27,12 +27,13 @@ from typing import TYPE_CHECKING -from PyQt5.QtCore import QEvent, QPoint, Qt, QSize, pyqtSignal from PyQt5.QtGui import QPalette +from PyQt5.QtCore import QEvent, QPoint, Qt, QSize, pyqtSignal from PyQt5.QtWidgets import QMenu, QToolButton, QVBoxLayout, QWidget from novelwriter import CONFIG, SHARED from novelwriter.enum import nwView +from novelwriter.extensions.eventfilters import StatusTipFilter if TYPE_CHECKING: # pragma: no cover from novelwriter.guimain import GuiMain @@ -54,6 +55,7 @@ def __init__(self, mainGui: GuiMain) -> None: iPx = CONFIG.pxInt(24) iconSize = QSize(iPx, iPx) self.setContentsMargins(0, 0, 0, 0) + self.installEventFilter(StatusTipFilter(mainGui)) # Buttons self.tbProject = QToolButton(self) @@ -162,7 +164,7 @@ def updateTheme(self) -> None: class _PopRightMenu(QMenu): - def event(self, event: QEvent): + def event(self, event: QEvent) -> bool: """Overload the show event and move the menu popup location.""" if event.type() == QEvent.Show: parent = self.parent() diff --git a/novelwriter/shared.py b/novelwriter/shared.py index 9bad600a3..5383765aa 100644 --- a/novelwriter/shared.py +++ b/novelwriter/shared.py @@ -45,7 +45,7 @@ class SharedData(QObject): __slots__ = ( - "_gui", "_theme", "_project", "_spelling", "_lockedBy", "_alert", + "_gui", "_theme", "_project", "_spelling", "_lockedBy", "_lastAlert", "_idleTime", "_idleRefTime", ) @@ -68,7 +68,7 @@ def __init__(self) -> None: # Settings self._lockedBy = None - self._alert = None + self._lastAlert = "" self._idleTime = 0.0 self._idleRefTime = time() @@ -122,9 +122,9 @@ def projectIdleTime(self) -> float: return self._idleTime @property - def alert(self) -> _GuiAlert | None: - """Return a pointer to the last alert box.""" - return self._alert + def lastAlert(self) -> str: + """Return the last alert message.""" + return self._lastAlert ## # Methods @@ -238,44 +238,53 @@ def indexSignalProxy(self, data: dict) -> None: def info(self, text: str, info: str = "", details: str = "", log: bool = True) -> None: """Open an information alert box.""" - self._alert = _GuiAlert(self.mainGui, self.theme) - self._alert.setMessage(text, info, details) - self._alert.setAlertType(_GuiAlert.INFO, False) + alert = _GuiAlert(self.mainGui, self.theme) + alert.setMessage(text, info, details) + alert.setAlertType(_GuiAlert.INFO, False) + self._lastAlert = alert.logMessage if log: - logger.info(self._alert.logMessage, stacklevel=2) - self._alert.exec_() + logger.info(self._lastAlert, stacklevel=2) + alert.exec_() + alert.deleteLater() return def warn(self, text: str, info: str = "", details: str = "", log: bool = True) -> None: """Open a warning alert box.""" - self._alert = _GuiAlert(self.mainGui, self.theme) - self._alert.setMessage(text, info, details) - self._alert.setAlertType(_GuiAlert.WARN, False) + alert = _GuiAlert(self.mainGui, self.theme) + alert.setMessage(text, info, details) + alert.setAlertType(_GuiAlert.WARN, False) + self._lastAlert = alert.logMessage if log: - logger.warning(self._alert.logMessage, stacklevel=2) - self._alert.exec_() + logger.warning(self._lastAlert, stacklevel=2) + alert.exec_() + alert.deleteLater() return def error(self, text: str, info: str = "", details: str = "", log: bool = True, exc: Exception | None = None) -> None: """Open an error alert box.""" - self._alert = _GuiAlert(self.mainGui, self.theme) - self._alert.setMessage(text, info, details) - self._alert.setAlertType(_GuiAlert.ERROR, False) + alert = _GuiAlert(self.mainGui, self.theme) + alert.setMessage(text, info, details) + alert.setAlertType(_GuiAlert.ERROR, False) if exc: - self._alert.setException(exc) + alert.setException(exc) + self._lastAlert = alert.logMessage if log: - logger.error(self._alert.logMessage, stacklevel=2) - self._alert.exec_() + logger.error(self._lastAlert, stacklevel=2) + alert.exec_() + alert.deleteLater() return def question(self, text: str, info: str = "", details: str = "", warn: bool = False) -> bool: """Open a question box.""" - self._alert = _GuiAlert(self.mainGui, self.theme) - self._alert.setMessage(text, info, details) - self._alert.setAlertType(_GuiAlert.WARN if warn else _GuiAlert.ASK, True) - self._alert.exec_() - return self._alert.result() == QMessageBox.Yes + alert = _GuiAlert(self.mainGui, self.theme) + alert.setMessage(text, info, details) + alert.setAlertType(_GuiAlert.WARN if warn else _GuiAlert.ASK, True) + self._lastAlert = alert.logMessage + alert.exec_() + isYes = alert.result() == QMessageBox.StandardButton.Yes + alert.deleteLater() + return isYes ## # Internal Functions @@ -312,6 +321,11 @@ def __init__(self, parent: QWidget, theme: GuiTheme) -> None: super().__init__(parent=parent) self._theme = theme self._message = "" + logger.debug("Ready: _GuiAlert") + return + + def __del__(self) -> None: # pragma: no cover + logger.debug("Delete: _GuiAlert") return @property diff --git a/novelwriter/tools/manuscript.py b/novelwriter/tools/manuscript.py index 5f7b68566..d68a74e7e 100644 --- a/novelwriter/tools/manuscript.py +++ b/novelwriter/tools/manuscript.py @@ -66,7 +66,7 @@ class GuiManuscript(QDialog): D_KEY = Qt.ItemDataRole.UserRole - def __init__(self, mainGui: GuiMain): + def __init__(self, mainGui: GuiMain) -> None: super().__init__(parent=mainGui) logger.debug("Create: GuiManuscript") @@ -173,7 +173,7 @@ def __init__(self, mainGui: GuiMain): self.btnBuild.clicked.connect(self._buildManuscript) self.btnClose = QPushButton(self.tr("Close")) - self.btnClose.clicked.connect(self._doClose) + self.btnClose.clicked.connect(self.close) self.processBox = QGridLayout() self.processBox.addWidget(self.btnPreview, 0, 0) @@ -217,11 +217,11 @@ def __init__(self, mainGui: GuiMain): return - def __del__(self): # pragma: no cover + def __del__(self) -> None: # pragma: no cover logger.debug("Delete: GuiManuscript") return - def loadContent(self): + def loadContent(self) -> None: """Load dialog content from project data.""" if len(self._builds) == 0: build = BuildSettings() @@ -255,7 +255,7 @@ def loadContent(self): # Events ## - def closeEvent(self, event: QCloseEvent): + def closeEvent(self, event: QCloseEvent) -> None: """Capture the user closing the window so we can save GUI settings. We also check that we don't have a build settings dialog open. @@ -274,7 +274,7 @@ def closeEvent(self, event: QCloseEvent): ## @pyqtSlot() - def _createNewBuild(self): + def _createNewBuild(self) -> None: """Open the build settings dialog for a new build.""" build = BuildSettings() build.setName(self.tr("My Manuscript")) @@ -282,7 +282,7 @@ def _createNewBuild(self): return @pyqtSlot() - def _editSelectedBuild(self): + def _editSelectedBuild(self) -> None: """Edit the currently selected build settings entry.""" build = self._getSelectedBuild() if build is not None: @@ -299,7 +299,7 @@ def _updateBuildDetails(self, current: QListWidgetItem, previous: QListWidgetIte return @pyqtSlot() - def _deleteSelectedBuild(self): + def _deleteSelectedBuild(self) -> None: """Delete the currently selected build settings entry.""" build = self._getSelectedBuild() if build is not None: @@ -309,7 +309,7 @@ def _deleteSelectedBuild(self): return @pyqtSlot(BuildSettings) - def _processNewSettings(self, build: BuildSettings): + def _processNewSettings(self, build: BuildSettings) -> None: """Process new build settings from the settings dialog.""" self._builds.setBuild(build) self._updateBuildItem(build) @@ -319,7 +319,7 @@ def _processNewSettings(self, build: BuildSettings): return @pyqtSlot() - def _generatePreview(self): + def _generatePreview(self) -> None: """Run the document builder on the current build settings for the preview widget. """ @@ -359,7 +359,7 @@ def _generatePreview(self): return @pyqtSlot() - def _buildManuscript(self): + def _buildManuscript(self) -> None: """Open the build dialog and build the manuscript.""" build = self._getSelectedBuild() if isinstance(build, BuildSettings): @@ -373,24 +373,18 @@ def _buildManuscript(self): return @pyqtSlot() - def _printDocument(self): + def _printDocument(self) -> None: """Open the print preview dialog.""" thePreview = QPrintPreviewDialog(self) thePreview.paintRequested.connect(self.docPreview.printPreview) thePreview.exec_() return - @pyqtSlot() - def _doClose(self): - """Forward the close button to the default close method.""" - self.close() - return - ## # Internal Functions ## - def _updatePreview(self, data: dict, build: BuildSettings): + def _updatePreview(self, data: dict, build: BuildSettings) -> None: """Update the preview widget and set relevant values.""" self.docPreview.setContent(data) self.docPreview.setBuildName(build.name) @@ -415,7 +409,7 @@ def _getSelectedBuild(self) -> BuildSettings | None: return build return None - def _saveSettings(self): + def _saveSettings(self) -> None: """Save the user GUI settings.""" buildOrder = [] for i in range(self.buildList.count()): @@ -454,7 +448,7 @@ def _saveSettings(self): return - def _openSettingsDialog(self, build: BuildSettings): + def _openSettingsDialog(self, build: BuildSettings) -> None: """Open the build settings dialog.""" for obj in self.mainGui.children(): # Don't open a second dialog if one exists @@ -475,7 +469,7 @@ def _openSettingsDialog(self, build: BuildSettings): return - def _updateBuildsList(self): + def _updateBuildsList(self) -> None: """Update the list of available builds.""" self.buildList.clear() for key, name in self._builds.builds(): @@ -487,7 +481,7 @@ def _updateBuildsList(self): self._buildMap[key] = bItem return - def _updateBuildItem(self, build: BuildSettings): + def _updateBuildItem(self, build: BuildSettings) -> None: """Update the entry of a specific build item.""" bItem = self._buildMap.get(build.buildID, None) if isinstance(bItem, QListWidgetItem): diff --git a/novelwriter/tools/projwizard.py b/novelwriter/tools/projwizard.py index b47da1cf3..5c3b18324 100644 --- a/novelwriter/tools/projwizard.py +++ b/novelwriter/tools/projwizard.py @@ -80,7 +80,7 @@ def __init__(self, mainGui): return - def __del__(self): # pragma: no cover + def __del__(self) -> None: # pragma: no cover logger.debug("Delete: GuiProjectWizard") return diff --git a/tests/test_base/test_base_common.py b/tests/test_base/test_base_common.py index 2ca1a35fa..785944d62 100644 --- a/tests/test_base/test_base_common.py +++ b/tests/test_base/test_base_common.py @@ -21,8 +21,6 @@ from __future__ import annotations import time -from PyQt5.QtCore import QUrl -from PyQt5.QtGui import QDesktopServices import pytest from pathlib import Path @@ -31,14 +29,16 @@ from tools import writeFile from mocked import causeOSError -from novelwriter.guimain import GuiMain +from PyQt5.QtGui import QDesktopServices +from PyQt5.QtCore import QUrl + from novelwriter.common import ( checkBool, checkFloat, checkHandle, checkInt, checkIntTuple, checkPath, checkString, checkStringNone, checkUuid, formatInt, formatTime, - formatTimeStamp, fuzzyTime, getFileSize, getGuiItem, hexToInt, isHandle, - isItemClass, isItemLayout, isItemType, isTitleTag, jsonEncode, - makeFileNameSafe, minmax, numberToRoman, NWConfigParser, openExternalPath, - readTextFile, simplified, transferCase, xmlIndent, yesNo + formatTimeStamp, fuzzyTime, getFileSize, hexToInt, isHandle, isItemClass, + isItemLayout, isItemType, isTitleTag, jsonEncode, makeFileNameSafe, minmax, + numberToRoman, NWConfigParser, openExternalPath, readTextFile, simplified, + transferCase, xmlIndent, yesNo ) @@ -655,15 +655,6 @@ def mockOpenUrl(url: QUrl) -> None: # END Test testBaseCommon_openExternalPath -@pytest.mark.base -def testBaseCommon_getGuiItem(nwGUI): - """Check the GUI item function.""" - assert getGuiItem("gibberish") is None - assert isinstance(getGuiItem("GuiMain"), GuiMain) - -# END Test testBaseCommon_getGuiItem - - @pytest.mark.base def testBaseCommon_NWConfigParser(fncPath): """Test the NWConfigParser subclass.""" diff --git a/tests/test_base/test_base_shared.py b/tests/test_base/test_base_shared.py index 4e3de75a0..0f105284b 100644 --- a/tests/test_base/test_base_shared.py +++ b/tests/test_base/test_base_shared.py @@ -22,13 +22,13 @@ import pytest +from tools import buildTestProject from mocked import MockGuiMain, MockTheme from PyQt5.QtWidgets import QMessageBox +from novelwriter.shared import SharedData from novelwriter.core.project import NWProject -from novelwriter.shared import SharedData, _GuiAlert -from tests.tools import buildTestProject @pytest.mark.base @@ -62,8 +62,6 @@ def testBaseSharedData_Init(): assert shared.projectIdleTime == 0.0 assert shared.projectLock is None - assert shared.alert is None - # END Test testBaseSharedData_Init @@ -124,7 +122,7 @@ def testBaseSharedData_Projects(fncPath, caplog): @pytest.mark.base -def testBaseSharedData_Alerts(monkeypatch, caplog): +def testBaseSharedData_Alerts(qtbot, monkeypatch, caplog): """Test SharedData class alert helper functions.""" monkeypatch.setattr(QMessageBox, "exec_", lambda *a: None) monkeypatch.setattr(QMessageBox, "result", lambda *a: QMessageBox.Yes) @@ -135,56 +133,38 @@ def testBaseSharedData_Alerts(monkeypatch, caplog): mockTheme = MockTheme() shared.initSharedData(mockGui, mockTheme) # type: ignore - assert shared.alert is None + assert shared.lastAlert == "" # Info box caplog.clear() shared.info("Hello World", info="foo", details="bar") - assert isinstance(shared.alert, _GuiAlert) - assert shared.alert.text() == "Hello World" - assert shared.alert.informativeText() == "foo" - assert shared.alert.detailedText() == "bar" + assert shared.lastAlert == "Hello World foo bar" assert caplog.text.strip().startswith("INFO") assert caplog.text.strip().endswith("Hello World foo bar") - shared._alert = None # Warning box caplog.clear() shared.warn("Oops!", info="foo", details="bar") - assert isinstance(shared.alert, _GuiAlert) - assert shared.alert.text() == "Oops!" - assert shared.alert.informativeText() == "foo" - assert shared.alert.detailedText() == "bar" + assert shared.lastAlert == "Oops! foo bar" assert caplog.text.strip().startswith("WARNING") assert caplog.text.strip().endswith("Oops! foo bar") - shared._alert = None # Error box caplog.clear() shared.error("Oh noes!", info="foo", details="bar") - assert isinstance(shared.alert, _GuiAlert) - assert shared.alert.text() == "Oh noes!" - assert shared.alert.informativeText() == "foo" - assert shared.alert.detailedText() == "bar" + assert shared.lastAlert == "Oh noes! foo bar" assert caplog.text.strip().startswith("ERROR") assert caplog.text.strip().endswith("Oh noes! foo bar") - shared._alert = None # Error box with exception caplog.clear() shared.error("Oh noes!", info="foo", details="bar", exc=Exception("Boom!")) - assert isinstance(shared.alert, _GuiAlert) - assert shared.alert.text() == "Oh noes!" - assert shared.alert.informativeText() == "foo
Exception: Boom!" - assert shared.alert.detailedText() == "bar" + assert shared.lastAlert == "Oh noes! foo bar" assert caplog.text.strip().startswith("ERROR") assert caplog.text.strip().endswith("Oh noes! foo bar") - shared._alert = None # Question box assert shared.question("Why?") is True - assert isinstance(shared.alert, _GuiAlert) - assert shared.alert.text() == "Why?" - shared._alert = None + assert shared.lastAlert == "Why?" # END Test testBaseSharedData_Alerts diff --git a/tests/test_core/test_core_project.py b/tests/test_core/test_core_project.py index 475c9ce18..6cf3b04ac 100644 --- a/tests/test_core/test_core_project.py +++ b/tests/test_core/test_core_project.py @@ -206,40 +206,35 @@ def testCoreProject_Open(monkeypatch, caplog, mockGUI, fncPath, mockRnd): mp.setattr(ProjectXMLReader, "read", lambda *a: False) mp.setattr(ProjectXMLReader, "state", property(lambda *a: XMLReadState.NOT_NWX_FILE)) assert theProject.openProject(fncPath) is False - lastMsg = SHARED.alert.logMessage if SHARED.alert else "" - assert "Project file does not appear" in lastMsg + assert "Project file does not appear" in SHARED.lastAlert # Unknown project file version with monkeypatch.context() as mp: mp.setattr(ProjectXMLReader, "read", lambda *a: False) mp.setattr(ProjectXMLReader, "state", property(lambda *a: XMLReadState.UNKNOWN_VERSION)) assert theProject.openProject(fncPath) is False - lastMsg = SHARED.alert.logMessage if SHARED.alert else "" - assert "Unknown or unsupported novelWriter project file" in lastMsg + assert "Unknown or unsupported novelWriter project file" in SHARED.lastAlert # Other parse error with monkeypatch.context() as mp: mp.setattr(ProjectXMLReader, "read", lambda *a: False) mp.setattr(ProjectXMLReader, "state", property(lambda *a: XMLReadState.CANNOT_PARSE)) assert theProject.openProject(fncPath) is False - lastMsg = SHARED.alert.logMessage if SHARED.alert else "" - assert "Failed to parse project xml" in lastMsg + assert "Failed to parse project xml" in SHARED.lastAlert # Won't convert legacy file with monkeypatch.context() as mp: mp.setattr(ProjectXMLReader, "state", property(lambda *a: XMLReadState.WAS_LEGACY)) mp.setattr(QMessageBox, "result", lambda *a: QMessageBox.No) assert theProject.openProject(fncPath) is False - lastMsg = SHARED.alert.logMessage if SHARED.alert else "" - assert "The file format of your project is about to be" in lastMsg + assert "The file format of your project is about to be" in SHARED.lastAlert # Won't open project from newer version with monkeypatch.context() as mp: mp.setattr(ProjectXMLReader, "hexVersion", property(lambda *a: 0x99999999)) mp.setattr(QMessageBox, "result", lambda *a: QMessageBox.No) assert theProject.openProject(fncPath) is False - lastMsg = SHARED.alert.logMessage if SHARED.alert else "" - assert "This project was saved by a newer version" in lastMsg + assert "This project was saved by a newer version" in SHARED.lastAlert # Fail checking items should still pass with monkeypatch.context() as mp: @@ -254,8 +249,7 @@ def testCoreProject_Open(monkeypatch, caplog, mockGUI, fncPath, mockRnd): mp.setattr("novelwriter.core.index.NWIndex.loadIndex", lambda *a: True) theProject.index._indexBroken = True assert theProject.openProject(fncPath) is True - lastMsg = SHARED.alert.logMessage if SHARED.alert else "" - assert "The file format of your project is about to be" in lastMsg + assert "The file format of your project is about to be" in SHARED.lastAlert assert theProject.index._indexBroken is False theProject.closeProject() diff --git a/tests/test_ext/test_ext_wheeleventfilter.py b/tests/test_ext/test_ext_eventfilters.py similarity index 92% rename from tests/test_ext/test_ext_wheeleventfilter.py rename to tests/test_ext/test_ext_eventfilters.py index 2d348358e..844d44036 100644 --- a/tests/test_ext/test_ext_wheeleventfilter.py +++ b/tests/test_ext/test_ext_eventfilters.py @@ -26,7 +26,7 @@ from PyQt5.QtCore import QEvent, QObject, QPoint, Qt from PyQt5.QtWidgets import QWidget -from novelwriter.extensions.wheeleventfilter import WheelEventFilter +from novelwriter.extensions.eventfilters import WheelEventFilter class MockWidget(QWidget): @@ -42,7 +42,7 @@ def wheelEvent(self, event: QWheelEvent) -> None: @pytest.mark.gui -def testExtWheelEventFilter_Main(): +def testExtEventFilters_WheelEventFilter(): """Test the WheelEventFilter class.""" obj = QObject() widget = MockWidget() @@ -65,4 +65,4 @@ def testExtWheelEventFilter_Main(): eFilter.eventFilter(obj, event) assert widget.count == 1 -# END Test testExtWheelEventFilter_Main +# END Test testExtEventFilters_WheelEventFilter diff --git a/tests/test_gui/test_gui_mainmenu.py b/tests/test_gui/test_gui_mainmenu.py index 721cc1306..91c1ac13f 100644 --- a/tests/test_gui/test_gui_mainmenu.py +++ b/tests/test_gui/test_gui_mainmenu.py @@ -656,8 +656,7 @@ def testGuiMenu_Insert(qtbot, monkeypatch, nwGUI, fncPath, projPath, mockRnd): nwGUI.mainMenu.aFileDetails.activate(QAction.Trigger) path = str(projPath / "content" / "000000000000f.nwd") - logMsg = SHARED.alert.logMessage if SHARED.alert else "" - assert logMsg.endswith(f"File Location: {path}") + assert SHARED.lastAlert.endswith(f"File Location: {path}") # qtbot.stop() diff --git a/tests/test_tools/test_tools_dictionaries.py b/tests/test_tools/test_tools_dictionaries.py index eee0f0f7b..02cc5dc01 100644 --- a/tests/test_tools/test_tools_dictionaries.py +++ b/tests/test_tools/test_tools_dictionaries.py @@ -44,8 +44,7 @@ def testToolDictionaries_Main(qtbot, monkeypatch, nwGUI, fncPath): with monkeypatch.context() as mp: mp.setattr(enchant, "get_user_config_dir", lambda *a: causeException) nwGUI.showDictionariesDialog() - assert SHARED.alert is not None - assert SHARED.alert.logMessage == "Could not initialise the dialog." + assert SHARED.lastAlert == "Could not initialise the dialog." # Open the tool nwGUI.showDictionariesDialog() @@ -57,17 +56,16 @@ def testToolDictionaries_Main(qtbot, monkeypatch, nwGUI, fncPath): assert nwDicts.inPath.text() == str(fncPath) # Allow Open Dir - SHARED._alert = None + SHARED._lastAlert = "" with monkeypatch.context() as mp: mp.setattr(QDesktopServices, "openUrl", lambda *a: None) nwDicts._doOpenInstallLocation() - assert SHARED.alert is None + assert SHARED.lastAlert == "" # Fail Open Dir nwDicts.inPath.setText("/foo/bar") nwDicts._doOpenInstallLocation() - assert SHARED.alert is not None - assert SHARED.alert.logMessage == "Path not found." + assert SHARED.lastAlert == "Path not found." nwDicts.inPath.setText(str(fncPath)) # Create Mock Dicts