Use a signal for status bar messages

This commit is contained in:
Veronica Berglyd Olsen
2023-08-14 00:03:55 +02:00
parent c4140d753c
commit 156a6fac98
9 changed files with 101 additions and 111 deletions
+3
View File
@@ -52,6 +52,9 @@ class nwConst:
URL_HELP = "https://github.com/vkbo/novelWriter/discussions"
URL_RELEASE = "https://github.com/vkbo/novelWriter/releases/latest"
# Gui Settings
STATUS_MSG_TIMEOUT = 15000 # milliseconds
# END Class nwConst
+7 -8
View File
@@ -58,7 +58,8 @@ logger = logging.getLogger(__name__)
class NWProject(QObject):
projectStatusChanged = pyqtSignal(bool)
statusChanged = pyqtSignal(bool)
statusMessage = pyqtSignal(str)
def __init__(self, mainGui: GuiMain) -> None:
super().__init__(parent=mainGui)
@@ -335,7 +336,7 @@ class NWProject(QObject):
self.setProjectChanged(False)
self._valid = True
self.mainGui.setStatus(self.tr("Opened Project: {0}").format(self._data.name))
self.statusMessage.emit(self.tr("Opened Project: {0}").format(self._data.name))
return True
@@ -389,7 +390,7 @@ class NWProject(QObject):
)
self._storage.writeLockFile()
self.mainGui.setStatus(self.tr("Saved Project: {0}").format(self._data.name))
self.statusMessage.emit(self.tr("Saved Project: {0}").format(self._data.name))
self.setProjectChanged(False)
return True
@@ -411,7 +412,7 @@ class NWProject(QObject):
return False
logger.info("Backing up project")
self.mainGui.setStatus(self.tr("Backing up project ..."))
self.statusMessage.emit(self.tr("Backing up project ..."))
if not self._data.name:
self.mainGui.makeAlert(self.tr(
@@ -446,9 +447,7 @@ class NWProject(QObject):
), level=nwAlert.ERROR)
return False
self.mainGui.setStatus(self.tr(
"Project backed up to '{0}'"
).format(str(archName)))
self.statusMessage.emit(self.tr("Project backed up to '{0}'").format(str(archName)))
return True
@@ -502,7 +501,7 @@ class NWProject(QObject):
"""
if isinstance(status, bool):
self._changed = status
self.projectStatusChanged.emit(self._changed)
self.statusChanged.emit(self._changed)
return self._changed
##
+9 -13
View File
@@ -71,9 +71,10 @@ class GuiDocEditor(QTextEdit):
)
# Custom Signals
spellDictionaryChanged = pyqtSignal(str, str)
docEditedStatusChanged = pyqtSignal(bool)
statusMessage = pyqtSignal(str)
docCountsChanged = pyqtSignal(str, int, int, int)
editedStatusChanged = pyqtSignal(bool)
spellDictionaryChanged = pyqtSignal(str, str)
loadDocumentTagRequest = pyqtSignal(str, Enum)
novelStructureChanged = pyqtSignal()
novelItemMetaChanged = pyqtSignal(str)
@@ -454,9 +455,7 @@ class GuiDocEditor(QTextEdit):
# Update the status bar
if self._nwItem is not None:
self.mainGui.setStatus(
self.tr("Opened Document: {0}").format(self._nwItem.itemName)
)
self.statusMessage.emit(self.tr("Opened Document: {0}").format(self._nwItem.itemName))
return True
@@ -562,9 +561,7 @@ class GuiDocEditor(QTextEdit):
self.docFooter.updateInfo()
# Update the status bar
self.mainGui.setStatus(
self.tr("Saved Document: {0}").format(self._nwItem.itemName)
)
self.statusMessage.emit(self.tr("Saved Document: {0}").format(self._nwItem.itemName))
return True
@@ -639,7 +636,7 @@ class GuiDocEditor(QTextEdit):
document change signal.
"""
self._docChanged = bValue
self.docEditedStatusChanged.emit(self._docChanged)
self.editedStatusChanged.emit(self._docChanged)
return self._docChanged
def setCursorPosition(self, position):
@@ -747,7 +744,7 @@ class GuiDocEditor(QTextEdit):
return True
def spellCheckDocument(self):
def spellCheckDocument(self) -> None:
"""Rerun the highlighter to update spell checking status of the
currently loaded text. The fastest way to do this, at least as
of Qt 5.13, is to clear the text and put it back. This clears
@@ -763,9 +760,8 @@ class GuiDocEditor(QTextEdit):
self.highLight.rehighlight()
qApp.restoreOverrideCursor()
logger.debug("Document highlighted in %.3f ms", 1000*(time() - start))
self.mainGui.mainStatus.setStatus(self.tr("Spell check complete"))
return True
self.statusMessage.emit(self.tr("Spell check complete"))
return
##
# General Class Methods
+57 -72
View File
@@ -27,6 +27,7 @@ from __future__ import annotations
import logging
from time import time
from typing import TYPE_CHECKING, Literal
from PyQt5.QtCore import pyqtSlot, QLocale
from PyQt5.QtGui import QColor
@@ -34,21 +35,24 @@ from PyQt5.QtWidgets import qApp, QStatusBar, QLabel
from novelwriter import CONFIG, SHARED
from novelwriter.common import formatTime
from novelwriter.constants import nwConst
from novelwriter.extensions.statusled import StatusLED
if TYPE_CHECKING: # pragma: no cover
from novelwriter.guimain import GuiMain
logger = logging.getLogger(__name__)
class GuiMainStatus(QStatusBar):
def __init__(self, mainGui):
def __init__(self, mainGui: GuiMain) -> None:
super().__init__(parent=mainGui)
logger.debug("Create: GuiMainStatus")
self.mainGui = mainGui
self.refTime = None
self.userIdle = False
self._refTime = -1.0
self._userIdle = False
colNone = QColor(*SHARED.theme.statNone)
colSaved = QColor(*SHARED.theme.statSaved)
@@ -114,80 +118,59 @@ class GuiMainStatus(QStatusBar):
return
def clearStatus(self):
"""Reset all widgets on the status bar to default values.
"""
self.setRefTime(None)
def clearStatus(self) -> None:
"""Reset all widgets on the status bar to default values."""
self.setRefTime(-1.0)
self.setLanguage(None, "")
self.setProjectStats(0, 0)
self.setProjectStatus(StatusLED.S_NONE)
self.setDocumentStatus(StatusLED.S_NONE)
self.updateTime()
return True
return
def updateTheme(self):
"""Update theme elements.
"""
def updateTheme(self) -> None:
"""Update theme elements."""
iPx = SHARED.theme.baseIconSize
self.langIcon.setPixmap(SHARED.theme.getPixmap("status_lang", (iPx, iPx)))
self.statsIcon.setPixmap(SHARED.theme.getPixmap("status_stats", (iPx, iPx)))
self.timePixmap = SHARED.theme.getPixmap("status_time", (iPx, iPx))
self.idlePixmap = SHARED.theme.getPixmap("status_idle", (iPx, iPx))
self.timeIcon.setPixmap(self.timePixmap)
return
##
# Setters
##
def setRefTime(self, theTime):
"""Set the reference time for the status bar clock.
"""
self.refTime = theTime
def setRefTime(self, refTime: float) -> None:
"""Set the reference time for the status bar clock."""
self._refTime = refTime
return
def setStatus(self, theMessage, timeOut=20.0):
"""Set the status bar message to display for 'timeOut' seconds.
"""
self.showMessage(theMessage, int(timeOut*1000))
qApp.processEvents()
def setProjectStatus(self, state: Literal[0, 1, 2]) -> None:
"""Set the project status colour icon."""
self.projIcon.setState(state)
return
def setProjectStatus(self, theState):
"""Set the project status colour icon.
"""
self.projIcon.setState(theState)
def setDocumentStatus(self, state: Literal[0, 1, 2]) -> None:
"""Set the document status colour icon."""
self.docIcon.setState(state)
return
def setDocumentStatus(self, theState):
"""Set the document status colour icon.
"""
self.docIcon.setState(theState)
return
def setUserIdle(self, userIdle):
"""Change the idle status icon.
"""
def setUserIdle(self, idle: bool) -> None:
"""Change the idle status icon."""
if not CONFIG.stopWhenIdle:
userIdle = False
if self.userIdle != userIdle:
if userIdle:
idle = False
if self._userIdle != idle:
if idle:
self.timeIcon.setPixmap(self.idlePixmap)
else:
self.timeIcon.setPixmap(self.timePixmap)
self.userIdle = userIdle
self._userIdle = idle
return
def setProjectStats(self, pWC, sWC):
"""Update the current project statistics.
"""
def setProjectStats(self, pWC: int, sWC: int) -> None:
"""Update the current project statistics."""
self.statsText.setText(self.tr("Words: {0} ({1})").format(f"{pWC:n}", f"{sWC:+n}"))
if CONFIG.incNotesWCount:
self.statsText.setToolTip(self.tr("Project word count (session change)"))
@@ -195,53 +178,55 @@ class GuiMainStatus(QStatusBar):
self.statsText.setToolTip(self.tr("Novel word count (session change)"))
return
def updateTime(self, idleTime=0.0):
"""Update the session clock.
"""
if self.refTime is None:
def updateTime(self, idleTime: float = 0.0) -> None:
"""Update the session clock."""
if self._refTime < 0.0:
self.timeText.setText("00:00:00")
else:
if CONFIG.stopWhenIdle:
sessTime = round(time() - self.refTime - idleTime)
sessTime = round(time() - self._refTime - idleTime)
else:
sessTime = round(time() - self.refTime)
sessTime = round(time() - self._refTime)
self.timeText.setText(formatTime(sessTime))
return
##
# Slots
# Public Slots
##
@pyqtSlot(str)
def setStatusMessage(self, message: str) -> None:
"""Set the status bar message to display."""
self.showMessage(message, nwConst.STATUS_MSG_TIMEOUT)
qApp.processEvents()
return
@pyqtSlot(str, str)
def setLanguage(self, theLanguage, theProvider):
"""Set the language code for the spell checker.
"""
if theLanguage == "None":
def setLanguage(self, language: str, provider: str) -> None:
"""Set the language code for the spell checker."""
if language == "None":
self.langText.setText(self.tr("None"))
self.langText.setToolTip("")
else:
qLocal = QLocale(theLanguage)
qLocal = QLocale(language)
spLang = qLocal.nativeLanguageName().title()
self.langText.setText(spLang)
if theProvider:
self.langText.setToolTip("%s (%s)" % (theLanguage, theProvider))
if provider:
self.langText.setToolTip("%s (%s)" % (language, provider))
else:
self.langText.setToolTip(theLanguage)
self.langText.setToolTip(language)
return
@pyqtSlot(bool)
def doUpdateProjectStatus(self, isChanged):
"""Slot for updating the project status.
"""
self.setProjectStatus(StatusLED.S_BAD if isChanged else StatusLED.S_GOOD)
def updateProjectStatus(self, status: bool) -> None:
"""Update the project status."""
self.setProjectStatus(StatusLED.S_BAD if status else StatusLED.S_GOOD)
return
@pyqtSlot(bool)
def doUpdateDocumentStatus(self, isChanged):
"""Slot for updating the document status.
"""
self.setDocumentStatus(StatusLED.S_BAD if isChanged else StatusLED.S_GOOD)
def updateDocumentStatus(self, status: bool) -> None:
"""Update the document status."""
self.setDocumentStatus(StatusLED.S_BAD if status else StatusLED.S_GOOD)
return
# END Class GuiMainStatus
+6 -7
View File
@@ -236,7 +236,8 @@ class GuiMain(QMainWindow):
# Connect Signals
# ===============
SHARED.projectStatusChanged.connect(self.mainStatus.doUpdateProjectStatus)
SHARED.projectStatusChanged.connect(self.mainStatus.updateProjectStatus)
SHARED.projectStatusMessage.connect(self.mainStatus.setStatusMessage)
self.viewsBar.viewChangeRequested.connect(self._changeView)
@@ -255,12 +256,13 @@ class GuiMain(QMainWindow):
self.novelView.openDocumentRequest.connect(self._openDocument)
self.docEditor.spellDictionaryChanged.connect(self.mainStatus.setLanguage)
self.docEditor.docEditedStatusChanged.connect(self.mainStatus.doUpdateDocumentStatus)
self.docEditor.editedStatusChanged.connect(self.mainStatus.updateDocumentStatus)
self.docEditor.docCountsChanged.connect(self.itemDetails.updateCounts)
self.docEditor.docCountsChanged.connect(self.projView.updateCounts)
self.docEditor.loadDocumentTagRequest.connect(self._followTag)
self.docEditor.novelStructureChanged.connect(self.novelView.refreshTree)
self.docEditor.novelItemMetaChanged.connect(self.novelView.updateNovelItemMeta)
self.docEditor.statusMessage.connect(self.mainStatus.setStatusMessage)
self.docViewer.loadDocumentTagRequest.connect(self._followTag)
@@ -299,9 +301,6 @@ class GuiMain(QMainWindow):
keyEscape.setKey(QKeySequence(Qt.Key_Escape))
keyEscape.activated.connect(self._keyPressEscape)
# Forward Functions
self.setStatus = self.mainStatus.setStatus
# Cache Alert Pixmaps
pxSize = (2*iPx, 2*iPx)
self.alertPix: dict[nwAlert, QPixmap] = {
@@ -333,7 +332,7 @@ class GuiMain(QMainWindow):
), level=nwAlert.WARN)
logger.info("novelWriter is ready ...")
self.setStatus(self.tr("novelWriter is ready ..."))
self.mainStatus.setStatusMessage(self.tr("novelWriter is ready ..."))
return
@@ -838,7 +837,7 @@ class GuiMain(QMainWindow):
self.novelView.refreshTree()
tEnd = time()
self.setStatus(
self.mainStatus.setStatusMessage(
self.tr("Indexing completed in {0} ms").format(f"{(tEnd - tStart)*1000.0:.1f}")
)
self.docEditor.updateTagHighLighting()
+12 -3
View File
@@ -41,6 +41,7 @@ logger = logging.getLogger(__name__)
class SharedData(QObject):
projectStatusChanged = pyqtSignal(bool)
projectStatusMessage = pyqtSignal(str)
def __init__(self) -> None:
super().__init__()
@@ -131,11 +132,17 @@ class SharedData(QObject):
##
@pyqtSlot(bool)
def _processProjectStatusChange(self, state: bool) -> None:
def _emitProjectStatusChange(self, state: bool) -> None:
"""Forward the project status slot."""
self.projectStatusChanged.emit(state)
return
@pyqtSlot(str)
def _emitProjectStatusMeesage(self, message: str) -> None:
"""Forward the project message slot."""
self.projectStatusMessage.emit(message)
return
##
# Internal Functions
##
@@ -144,10 +151,12 @@ class SharedData(QObject):
"""Create a new project instance."""
from novelwriter.core.project import NWProject
if isinstance(self._project, NWProject):
self._project.projectStatusChanged.disconnect()
self._project.statusChanged.disconnect()
self._project.statusMessage.disconnect()
self._project.deleteLater()
self._project = NWProject(self.mainGui)
self._project.projectStatusChanged.connect(self._processProjectStatusChange)
self._project.statusChanged.connect(self._emitProjectStatusChange)
self._project.statusMessage.connect(self._emitProjectStatusMeesage)
return
# END Class SharedData
+1 -1
View File
@@ -92,7 +92,7 @@ class MockStatusBar:
def setStatus(self, theText):
return
def doUpdateProjectStatus(self, theStatus):
def updateProjectStatus(self, theStatus):
return
# END Class MockStatusBar
+1 -1
View File
@@ -213,7 +213,7 @@ def testGuiEditor_MetaData(qtbot, nwGUI, projPath, mockRnd):
# Document Changed Signal
nwGUI.docEditor._docChanged = False
with qtbot.waitSignal(nwGUI.docEditor.docEditedStatusChanged, raising=True, timeout=100):
with qtbot.waitSignal(nwGUI.docEditor.editedStatusChanged, raising=True, timeout=100):
nwGUI.docEditor.setDocumentChanged(True)
assert nwGUI.docEditor._docChanged is True
+5 -6
View File
@@ -30,8 +30,7 @@ from novelwriter.extensions.statusled import StatusLED
@pytest.mark.gui
def testGuiStatusBar_Main(qtbot, nwGUI, projPath, mockRnd):
"""Test the the various features of the status bar.
"""
"""Test the the various features of the status bar."""
buildTestProject(nwGUI, projPath)
cHandle = nwGUI.project.newFile("A Note", C.hCharRoot)
newDoc = nwGUI.project.storage.getDocument(cHandle)
@@ -42,7 +41,7 @@ def testGuiStatusBar_Main(qtbot, nwGUI, projPath, mockRnd):
# Reference Time
refTime = time.time()
nwGUI.mainStatus.setRefTime(refTime)
assert nwGUI.mainStatus.refTime == refTime
assert nwGUI.mainStatus._refTime == refTime
# Project Status
nwGUI.mainStatus.setProjectStatus(StatusLED.S_NONE)
@@ -64,18 +63,18 @@ def testGuiStatusBar_Main(qtbot, nwGUI, projPath, mockRnd):
CONFIG.stopWhenIdle = False
nwGUI.mainStatus.setUserIdle(True)
nwGUI.mainStatus.updateTime()
assert nwGUI.mainStatus.userIdle is False
assert nwGUI.mainStatus._userIdle is False
assert nwGUI.mainStatus.timeText.text() == "00:00:00"
CONFIG.stopWhenIdle = True
nwGUI.mainStatus.setUserIdle(True)
nwGUI.mainStatus.updateTime(5)
assert nwGUI.mainStatus.userIdle is True
assert nwGUI.mainStatus._userIdle is True
assert nwGUI.mainStatus.timeText.text() != "00:00:00"
nwGUI.mainStatus.setUserIdle(False)
nwGUI.mainStatus.updateTime(5)
assert nwGUI.mainStatus.userIdle is False
assert nwGUI.mainStatus._userIdle is False
assert nwGUI.mainStatus.timeText.text() != "00:00:00"
# Language