diff --git a/novelwriter/common.py b/novelwriter/common.py index 0f61bd89..91b2fae9 100644 --- a/novelwriter/common.py +++ b/novelwriter/common.py @@ -38,7 +38,6 @@ from urllib.request import pathname2url 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 f66f4549..1f74bb43 100644 --- a/novelwriter/core/project.py +++ b/novelwriter/core/project.py @@ -80,7 +80,7 @@ class NWProject: 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 e2a022e9..852fbd48 100644 --- a/novelwriter/core/spellcheck.py +++ b/novelwriter/core/spellcheck.py @@ -56,7 +56,7 @@ class NWSpellEnchant: 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 220fa324..9c53354e 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 @@ along with this program. If not, see . """ 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 @@ class WheelEventFilter(QObject): 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 4d4ac393..8ea95769 100644 --- a/novelwriter/gui/doceditor.py +++ b/novelwriter/gui/doceditor.py @@ -62,7 +62,7 @@ from novelwriter.tools.lipsum import GuiLipsum 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 d548f0d4..44ccb1d5 100644 --- a/novelwriter/gui/docviewer.py +++ b/novelwriter/gui/docviewer.py @@ -46,7 +46,7 @@ from novelwriter.enum import nwItemType, nwDocAction, nwDocMode 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 aa00ec7c..3f6661e0 100644 --- a/novelwriter/gui/editordocument.py +++ b/novelwriter/gui/editordocument.py @@ -50,7 +50,7 @@ class GuiTextDocument(QTextDocument): 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 a601fb99..e442f9e8 100644 --- a/novelwriter/gui/mainmenu.py +++ b/novelwriter/gui/mainmenu.py @@ -3,8 +3,7 @@ novelWriter – GUI Main Menu =========================== 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 @@ import logging 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 @@ class GuiMainMenu(QMenuBar): 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 607e694a..c2355707 100644 --- a/novelwriter/gui/projtree.py +++ b/novelwriter/gui/projtree.py @@ -1650,7 +1650,7 @@ class _TreeContextMenu(QMenu): 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 9cfbf118..b15908a2 100644 --- a/novelwriter/gui/sidebar.py +++ b/novelwriter/gui/sidebar.py @@ -27,12 +27,13 @@ import logging 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 @@ class GuiSideBar(QWidget): 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 @@ class GuiSideBar(QWidget): 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 9bad600a..5383765a 100644 --- a/novelwriter/shared.py +++ b/novelwriter/shared.py @@ -45,7 +45,7 @@ logger = logging.getLogger(__name__) class SharedData(QObject): __slots__ = ( - "_gui", "_theme", "_project", "_spelling", "_lockedBy", "_alert", + "_gui", "_theme", "_project", "_spelling", "_lockedBy", "_lastAlert", "_idleTime", "_idleRefTime", ) @@ -68,7 +68,7 @@ class SharedData(QObject): # Settings self._lockedBy = None - self._alert = None + self._lastAlert = "" self._idleTime = 0.0 self._idleRefTime = time() @@ -122,9 +122,9 @@ class SharedData(QObject): 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 @@ class SharedData(QObject): 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 @@ class _GuiAlert(QMessageBox): 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 5f7b6856..d68a74e7 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 @@ class GuiManuscript(QDialog): 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 @@ class GuiManuscript(QDialog): 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 @@ class GuiManuscript(QDialog): # 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 @@ class GuiManuscript(QDialog): ## @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 @@ class GuiManuscript(QDialog): 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 @@ class GuiManuscript(QDialog): 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 @@ class GuiManuscript(QDialog): 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 @@ class GuiManuscript(QDialog): 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 @@ class GuiManuscript(QDialog): 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 @@ class GuiManuscript(QDialog): 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 @@ class GuiManuscript(QDialog): 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 @@ class GuiManuscript(QDialog): 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 @@ class GuiManuscript(QDialog): 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 @@ class GuiManuscript(QDialog): 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 b47da1cf..5c3b1832 100644 --- a/novelwriter/tools/projwizard.py +++ b/novelwriter/tools/projwizard.py @@ -80,7 +80,7 @@ class GuiProjectWizard(QWizard): 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 2ca1a35f..785944d6 100644 --- a/tests/test_base/test_base_common.py +++ b/tests/test_base/test_base_common.py @@ -21,8 +21,6 @@ along with this program. If not, see . 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 xml.etree import ElementTree as ET 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 testBaseCommon_openExternalPath(monkeypatch, tstPaths): # 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 4e3de75a..0f105284 100644 --- a/tests/test_base/test_base_shared.py +++ b/tests/test_base/test_base_shared.py @@ -22,13 +22,13 @@ from __future__ import annotations 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 475c9ce1..6cf3b04a 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 2d348358..844d4403 100644 --- a/tests/test_ext/test_ext_wheeleventfilter.py +++ b/tests/test_ext/test_ext_eventfilters.py @@ -26,7 +26,7 @@ from PyQt5.QtGui import QKeyEvent, QWheelEvent 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 @@ class MockWidget(QWidget): @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 721cc130..91c1ac13 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 eee0f0f7..02cc5dc0 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