diff --git a/novelwriter/common.py b/novelwriter/common.py index 64a4de1b..1dd2c64c 100644 --- a/novelwriter/common.py +++ b/novelwriter/common.py @@ -38,7 +38,7 @@ from urllib.parse import urljoin from urllib.request import pathname2url from PyQt5.QtCore import QCoreApplication, QMimeData, QUrl -from PyQt5.QtGui import QColor, QDesktopServices, QFont, QFontInfo +from PyQt5.QtGui import QColor, QDesktopServices, QFont, QFontDatabase, QFontInfo from novelwriter.constants import nwConst, nwLabels, nwUnicode, trConst from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType @@ -434,6 +434,30 @@ def describeFont(font: QFont) -> str: return "Error" +def fontMatcher(font: QFont) -> QFont: + """Make sure the font is the correct family, if possible. This + ensures that Qt doesn't re-use another font under the hood. The + default Qt5 font matching algorithm doesn't handle well changing + application fonts at runtime. + """ + info = QFontInfo(font) + if (famRequest := font.family()) != (famActual := info.family()): + logger.warning("Font mismatch: Requested '%s', but got '%s'", famRequest, famActual) + db = QFontDatabase() + if famRequest in db.families(): + styleRequest, sizeRequest = font.styleName(), font.pointSize() + logger.info("Lookup: %s, %s, %d pt", famRequest, styleRequest, sizeRequest) + temp = db.font(famRequest, styleRequest, sizeRequest) + temp.setPointSize(sizeRequest) # Make sure it isn't changed + famFound, styleFound, sizeFound = temp.family(), temp.styleName(), temp.pointSize() + if famFound == famRequest: + logger.info("Found: %s, %s, %d pt", famFound, styleFound, sizeFound) + return temp + logger.warning("Could not find a font match in the font database") + logger.warning("If you just changed font, you may need to restart the application") + return font + + def qtLambda(func: Callable, *args: Any, **kwargs: Any) -> Callable: """A replacement for Python lambdas that works for Qt slots.""" def wrapper(*a_: Any) -> None: @@ -441,6 +465,12 @@ def qtLambda(func: Callable, *args: Any, **kwargs: Any) -> Callable: return wrapper +def encodeMimeHandles(mimeData: QMimeData, handles: list[str]) -> None: + """Encode handles into a mime data object.""" + mimeData.setData(nwConst.MIME_HANDLE, b"|".join(h.encode() for h in handles)) + return + + def decodeMimeHandles(mimeData: QMimeData) -> list[str]: """Decode and split a mime data object with handles.""" return mimeData.data(nwConst.MIME_HANDLE).data().decode().split("|") diff --git a/novelwriter/config.py b/novelwriter/config.py index 45dc01d9..d1ae57b5 100644 --- a/novelwriter/config.py +++ b/novelwriter/config.py @@ -40,7 +40,10 @@ from PyQt5.QtCore import ( from PyQt5.QtGui import QFont, QFontDatabase from PyQt5.QtWidgets import QApplication -from novelwriter.common import NWConfigParser, checkInt, checkPath, describeFont, formatTimeStamp +from novelwriter.common import ( + NWConfigParser, checkInt, checkPath, describeFont, fontMatcher, + formatTimeStamp +) from novelwriter.constants import nwFiles, nwUnicode from novelwriter.error import formatException, logException @@ -369,10 +372,11 @@ class Config: def setGuiFont(self, value: QFont | str | None) -> None: """Update the GUI's font style from settings.""" if isinstance(value, QFont): - self.guiFont = value + self.guiFont = fontMatcher(value) elif value and isinstance(value, str): - self.guiFont = QFont() - self.guiFont.fromString(value) + font = QFont() + font.fromString(value) + self.guiFont = fontMatcher(font) else: font = QFont() fontDB = QFontDatabase() @@ -382,11 +386,9 @@ class Config: font.setPointSize(10) else: font = fontDB.systemFont(QFontDatabase.SystemFont.GeneralFont) - self.guiFont = font + self.guiFont = fontMatcher(font) logger.debug("GUI font set to: %s", describeFont(font)) - QApplication.setFont(self.guiFont) - return def setTextFont(self, value: QFont | str | None) -> None: @@ -394,10 +396,11 @@ class Config: set to default font. """ if isinstance(value, QFont): - self.textFont = value + self.textFont = fontMatcher(value) elif value and isinstance(value, str): - self.textFont = QFont() - self.textFont.fromString(value) + font = QFont() + font.fromString(value) + self.textFont = fontMatcher(font) else: fontDB = QFontDatabase() fontFam = fontDB.families() @@ -411,8 +414,8 @@ class Config: font.setPointSize(12) else: font = fontDB.systemFont(QFontDatabase.SystemFont.GeneralFont) - self.textFont = font - logger.debug("Text font set to: %s", describeFont(font)) + self.textFont = fontMatcher(font) + logger.debug("Text font set to: %s", describeFont(self.textFont)) return ## diff --git a/novelwriter/core/docbuild.py b/novelwriter/core/docbuild.py index 5cbdfc0a..a98da986 100644 --- a/novelwriter/core/docbuild.py +++ b/novelwriter/core/docbuild.py @@ -178,7 +178,7 @@ class NWBuildDocument: makeObj = ToQTextDocument(self._project) makeObj.disableAnchors() filtered = self._setupBuild(makeObj) - makeObj.initDocument() + makeObj.initDocument(pdf=True) yield from self._iterBuild(makeObj, filtered) makeObj.closeDocument() @@ -218,7 +218,7 @@ class NWBuildDocument: textFont = QFont(CONFIG.textFont) textFont.fromString(self._build.getStr("format.textFont")) - bldObj.setFont(textFont) + bldObj.setTextFont(textFont) bldObj.setLanguage(self._project.data.language) bldObj.setPartitionFormat( diff --git a/novelwriter/core/itemmodel.py b/novelwriter/core/itemmodel.py index df4d33c1..f8784ac2 100644 --- a/novelwriter/core/itemmodel.py +++ b/novelwriter/core/itemmodel.py @@ -31,7 +31,7 @@ from typing import TYPE_CHECKING from PyQt5.QtCore import QAbstractItemModel, QMimeData, QModelIndex, Qt from PyQt5.QtGui import QFont, QIcon -from novelwriter.common import decodeMimeHandles, minmax +from novelwriter.common import decodeMimeHandles, encodeMimeHandles, minmax from novelwriter.constants import nwConst from novelwriter.core.item import NWItem from novelwriter.enum import nwItemClass @@ -367,11 +367,11 @@ class ProjectModel(QAbstractItemModel): def mimeData(self, indices: list[QModelIndex]) -> QMimeData: """Encode mime data about a selection.""" handles = [ - i.internalPointer().item.itemHandle.encode() + i.internalPointer().item.itemHandle for i in indices if i.isValid() and i.column() == 0 ] mime = QMimeData() - mime.setData(nwConst.MIME_HANDLE, b"|".join(handles)) + encodeMimeHandles(mime, handles) return mime def canDropMimeData( diff --git a/novelwriter/formats/tokenizer.py b/novelwriter/formats/tokenizer.py index 9af17216..2b75130d 100644 --- a/novelwriter/formats/tokenizer.py +++ b/novelwriter/formats/tokenizer.py @@ -35,7 +35,7 @@ from PyQt5.QtCore import QLocale from PyQt5.QtGui import QColor, QFont from novelwriter import CONFIG -from novelwriter.common import checkInt, numberToRoman +from novelwriter.common import checkInt, fontMatcher, numberToRoman from novelwriter.constants import ( nwHeadFmt, nwKeyWords, nwLabels, nwShortcode, nwStats, nwStyles, nwUnicode, trConst @@ -302,9 +302,9 @@ class Tokenizer(ABC): self._sceneStyle |= BlockFmt.PBB if pageBreak else BlockFmt.NONE return - def setFont(self, font: QFont) -> None: + def setTextFont(self, font: QFont) -> None: """Set the build font.""" - self._textFont = font + self._textFont = fontMatcher(font) return def setLineHeight(self, height: float) -> None: diff --git a/novelwriter/formats/toodt.py b/novelwriter/formats/toodt.py index 37ba5943..dedec194 100644 --- a/novelwriter/formats/toodt.py +++ b/novelwriter/formats/toodt.py @@ -43,7 +43,7 @@ from novelwriter.constants import nwHeadFmt, nwStyles from novelwriter.core.project import NWProject from novelwriter.formats.shared import BlockFmt, BlockTyp, TextFmt, stripEscape from novelwriter.formats.tokenizer import Tokenizer -from novelwriter.types import FONT_STYLE, FONT_WEIGHTS, QtHexRgb +from novelwriter.types import FONT_STYLE, QtHexRgb logger = logging.getLogger(__name__) @@ -220,20 +220,14 @@ class ToOdt(Tokenizer): # Initialise Variables # ==================== - intWeight = FONT_WEIGHTS.get(self._textFont.weight(), 400) - fontWeight = str(intWeight) - fontBold = str(min(intWeight + 300, 900)) - lang, _, country = self._dLocale.name().partition("_") self._dLanguage = lang or self._dLanguage self._dCountry = country or self._dCountry self._fontFamily = self._textFont.family() self._fontSize = self._textFont.pointSize() - self._fontWeight = FONT_WEIGHT_MAP.get(fontWeight, fontWeight) self._fontStyle = FONT_STYLE.get(self._textFont.style(), "normal") self._fontPitch = "fixed" if self._textFont.fixedPitch() else "variable" - self._fontBold = FONT_WEIGHT_MAP.get(fontBold, fontBold) self._headWeight = self._fontBold if self._boldHeads else None self._fBlockIndent = self._emToCm(self._blockIndent) diff --git a/novelwriter/formats/toqdoc.py b/novelwriter/formats/toqdoc.py index 1787413e..0c11168f 100644 --- a/novelwriter/formats/toqdoc.py +++ b/novelwriter/formats/toqdoc.py @@ -29,8 +29,8 @@ from pathlib import Path from PyQt5.QtCore import QMarginsF, QSizeF from PyQt5.QtGui import ( - QColor, QFont, QPageSize, QTextBlockFormat, QTextCharFormat, QTextCursor, - QTextDocument + QColor, QFont, QFontDatabase, QPageSize, QTextBlockFormat, QTextCharFormat, + QTextCursor, QTextDocument, QTextFrameFormat ) from PyQt5.QtPrintSupport import QPrinter @@ -74,11 +74,16 @@ class ToQTextDocument(Tokenizer): self._usedFields: list[tuple[int, str]] = [] self._init = False - self._bold = QFont.Weight.Bold - self._normal = QFont.Weight.Normal self._newPage = False self._anchors = True + self._hWeight = QFont.Weight.Bold + self._dWeight = QFont.Weight.Normal + self._dItalic = False + self._dStrike = False + self._dUnderline = False + + self._dpi = 96 self._pageSize = QPageSize(QPageSize.PageSizeId.A4) self._pageMargins = QMarginsF(20.0, 20.0, 20.0, 20.0) @@ -119,21 +124,37 @@ class ToQTextDocument(Tokenizer): # Class Methods ## - def initDocument(self) -> None: + def initDocument(self, pdf: bool = False) -> None: """Initialise all computed values of the document.""" super().initDocument() + if pdf: + fontDB = QFontDatabase() + family = self._textFont.family() + style = self._textFont.styleName() + self._dpi = 1200 if fontDB.isScalable(family, style) else 72 + self._document.setUndoRedoEnabled(False) self._document.blockSignals(True) self._document.clear() self._document.setDefaultFont(self._textFont) - fPt = self._textFont.pointSizeF() - fPx = fPt*96.0/72.0 # 1 em in pixels + # Default Styles + self._dWeight = self._textFont.weight() + self._dItalic = self._textFont.italic() + self._dStrike = self._textFont.strikeOut() + self._dUnderline = self._textFont.underline() + + # Header Weight + self._hWeight = QFont.Weight.Bold if self._boldHeads else self._dWeight # Scaled Sizes # ============ + fPt = self._textFont.pointSizeF() + fPx = fPt*96.0/72.0 # 1 em in pixels + mPx = fPx * self._dpi/96.0 + self._mHead = { BlockTyp.TITLE: (fPx * self._marginTitle[0], fPx * self._marginTitle[1]), BlockTyp.HEAD1: (fPx * self._marginHead1[0], fPx * self._marginHead1[1]), @@ -155,8 +176,8 @@ class ToQTextDocument(Tokenizer): self._mMeta = (fPx * self._marginMeta[0], fPx * self._marginMeta[1]) self._mSep = (fPx * self._marginSep[0], fPx * self._marginSep[1]) - self._mIndent = fPx * 2.0 - self._tIndent = fPx * self._firstWidth + self._mIndent = mPx * 2.0 + self._tIndent = mPx * self._firstWidth # Text Formats # ============ @@ -248,10 +269,12 @@ class ToQTextDocument(Tokenizer): def saveDocument(self, path: Path) -> None: """Save the document as a PDF file.""" m = self._pageMargins + logger.info("Writing PDF at %d DPI", self._dpi) printer = QPrinter(QPrinter.PrinterMode.HighResolution) printer.setDocName(self._project.data.name) printer.setCreator(f"novelWriter/{__version__}") + printer.setResolution(self._dpi) printer.setOutputFormat(QPrinter.OutputFormat.PdfFormat) printer.setPageSize(self._pageSize) printer.setPageMargins(m.left(), m.top(), m.right(), m.bottom(), QPrinter.Unit.Millimeter) @@ -318,21 +341,21 @@ class ToQTextDocument(Tokenizer): # Construct next format if fmt == TextFmt.B_B: - cFmt.setFontWeight(self._bold) + cFmt.setFontWeight(QFont.Weight.Bold) elif fmt == TextFmt.B_E: - cFmt.setFontWeight(self._normal) + cFmt.setFontWeight(self._dWeight) elif fmt == TextFmt.I_B: cFmt.setFontItalic(True) elif fmt == TextFmt.I_E: - cFmt.setFontItalic(False) + cFmt.setFontItalic(self._dItalic) elif fmt == TextFmt.D_B: cFmt.setFontStrikeOut(True) elif fmt == TextFmt.D_E: - cFmt.setFontStrikeOut(False) + cFmt.setFontStrikeOut(self._dStrike) elif fmt == TextFmt.U_B: cFmt.setFontUnderline(True) elif fmt == TextFmt.U_E: - cFmt.setFontUnderline(False) + cFmt.setFontUnderline(self._dUnderline) elif fmt == TextFmt.M_B: cFmt.setBackground(self._theme.highlight) elif fmt == TextFmt.M_E: @@ -376,7 +399,7 @@ class ToQTextDocument(Tokenizer): cFmt.setAnchorHref(data) elif fmt == TextFmt.HRF_E: cFmt.setForeground(primary or self._theme.text) - cFmt.setFontUnderline(False) + cFmt.setFontUnderline(self._dUnderline) cFmt.setAnchor(False) cFmt.setAnchorHref("") elif fmt == TextFmt.FNOTE: @@ -409,24 +432,36 @@ class ToQTextDocument(Tokenizer): def _insertNewPageMarker(self, cursor: QTextCursor) -> None: """Insert a new page marker.""" if self._newPage: - cursor.insertHtml("
") + bgCol = QColor(self._theme.text) + bgCol.setAlphaF(0.1) + fgCol = QColor(self._theme.text) + fgCol.setAlphaF(0.8) - hFmt = cursor.blockFormat() - hFmt.setBottomMargin(0.0) - hFmt.setLineHeight(75.0, QtPropLineHeight) - cursor.setBlockFormat(hFmt) + fFmt = QTextFrameFormat() + fFmt.setBorderStyle(QTextFrameFormat.BorderStyle.BorderStyle_None) + fFmt.setBackground(bgCol) + fFmt.setTopMargin(self._mSep[0]) + fFmt.setBottomMargin(self._mSep[1]) bFmt = QTextBlockFormat(self._blockFmt) bFmt.setAlignment(QtAlignCenter) bFmt.setTopMargin(0.0) - bFmt.setLineHeight(75.0, QtPropLineHeight) + bFmt.setBottomMargin(0.0) + bFmt.setLineHeight(100.0, QtPropLineHeight) cFmt = QTextCharFormat(self._charFmt) + cFmt.setFontItalic(False) + cFmt.setFontUnderline(False) + cFmt.setFontStrikeOut(False) + cFmt.setFontWeight(QFont.Weight.Normal) cFmt.setFontPointSize(0.75*self._textFont.pointSizeF()) - cFmt.setForeground(self._theme.comment) + cFmt.setForeground(fgCol) - newBlock(cursor, bFmt) + cursor.insertFrame(fFmt) + cursor.setBlockFormat(bFmt) cursor.insertText(self._project.localLookup("New Page"), cFmt) + cursor.swap(self._document.rootFrame().lastCursorPosition()) + return def _genHeadStyle(self, hType: BlockTyp, hKey: str, rFmt: QTextBlockFormat) -> T_TextStyle: @@ -440,7 +475,7 @@ class ToQTextDocument(Tokenizer): hCol = self._colorHeads and hType != BlockTyp.TITLE cFmt = QTextCharFormat(self._charFmt) cFmt.setForeground(self._theme.head if hCol else self._theme.text) - cFmt.setFontWeight(self._bold if self._boldHeads else self._normal) + cFmt.setFontWeight(self._hWeight) cFmt.setFontPointSize(self._sHead.get(hType, 1.0)) if hKey and self._anchors: cFmt.setAnchorNames([hKey]) diff --git a/novelwriter/gui/doceditor.py b/novelwriter/gui/doceditor.py index a77869ca..1e53c7f7 100644 --- a/novelwriter/gui/doceditor.py +++ b/novelwriter/gui/doceditor.py @@ -53,7 +53,7 @@ from PyQt5.QtWidgets import ( ) from novelwriter import CONFIG, SHARED -from novelwriter.common import decodeMimeHandles, minmax, qtLambda, transferCase +from novelwriter.common import decodeMimeHandles, fontMatcher, minmax, qtLambda, transferCase from novelwriter.constants import nwConst, nwKeyWords, nwShortcode, nwUnicode from novelwriter.core.document import NWDocument from novelwriter.enum import ( @@ -348,7 +348,9 @@ class GuiDocEditor(QPlainTextEdit): SHARED.updateSpellCheckLanguage() # Set the font. See issues #1862 and #1875. - self.setFont(CONFIG.textFont) + font = fontMatcher(CONFIG.textFont) + self.setFont(font) + self._qDocument.setDefaultFont(font) self.docHeader.updateFont() self.docFooter.updateFont() self.docSearch.updateFont() @@ -1277,11 +1279,13 @@ class GuiDocEditor(QPlainTextEdit): """Process the word counter's finished signal.""" if self._docHandle and self._nwItem: logger.debug("Updating word count") + needsRefresh = wCount != self._nwItem.wordCount self._nwItem.setCharCount(cCount) self._nwItem.setWordCount(wCount) self._nwItem.setParaCount(pCount) - self._nwItem.notifyToRefresh() - self.docFooter.updateWordCount(wCount, False) + if needsRefresh: + self._nwItem.notifyToRefresh() + self.docFooter.updateWordCount(wCount, False) return @pyqtSlot() diff --git a/novelwriter/gui/docviewer.py b/novelwriter/gui/docviewer.py index 9e1b6f5f..d57303a6 100644 --- a/novelwriter/gui/docviewer.py +++ b/novelwriter/gui/docviewer.py @@ -219,7 +219,7 @@ class GuiDocViewer(QTextBrowser): qDoc = ToQTextDocument(SHARED.project) qDoc.setJustify(CONFIG.doJustify) qDoc.setDialogHighlight(True) - qDoc.setFont(CONFIG.textFont) + qDoc.setTextFont(CONFIG.textFont) qDoc.setTheme(self._docTheme) qDoc.initDocument() qDoc.setKeywords(True) diff --git a/novelwriter/guimain.py b/novelwriter/guimain.py index 8d83bd92..d0f74bdc 100644 --- a/novelwriter/guimain.py +++ b/novelwriter/guimain.py @@ -1051,11 +1051,6 @@ class GuiMain(QMainWindow): self.initMain() self.saveDocument() - if restart: - SHARED.info(self.tr( - "Some changes will not be applied until novelWriter has been restarted." - )) - if tree: SHARED.project.tree.refreshAllItems() @@ -1088,6 +1083,11 @@ class GuiMain(QMainWindow): self._lastTotalCount = 0 self._updateStatusWordCount() + if restart: + SHARED.info(self.tr( + "Some changes will not be applied until novelWriter has been restarted." + )) + return @pyqtSlot() diff --git a/novelwriter/tools/manussettings.py b/novelwriter/tools/manussettings.py index e5221cd2..dc64a316 100644 --- a/novelwriter/tools/manussettings.py +++ b/novelwriter/tools/manussettings.py @@ -37,7 +37,7 @@ from PyQt5.QtWidgets import ( ) from novelwriter import CONFIG, SHARED -from novelwriter.common import describeFont, qtLambda +from novelwriter.common import describeFont, fontMatcher, qtLambda from novelwriter.constants import nwHeadFmt, nwKeyWords, nwLabels, nwStyles, trConst from novelwriter.core.buildsettings import BuildSettings, FilterMode from novelwriter.extensions.configlayout import ( @@ -1281,8 +1281,9 @@ class _FormattingTab(NScrollableForm): # Text Format # =========== - self._textFont = QFont() - self._textFont.fromString(self._build.getStr("format.textFont")) + font = QFont() + font.fromString(self._build.getStr("format.textFont")) + self._textFont = fontMatcher(font) self.textFont.setText(describeFont(self._textFont)) self.textFont.setCursorPosition(0) @@ -1437,9 +1438,9 @@ class _FormattingTab(NScrollableForm): """Open the QFontDialog and set a font for the font style.""" font, status = SHARED.getFont(self._textFont, CONFIG.nativeFont) if status: - self.textFont.setText(describeFont(font)) + self._textFont = fontMatcher(font) + self.textFont.setText(describeFont(self._textFont)) self.textFont.setCursorPosition(0) - self._textFont = font return @pyqtSlot(int) diff --git a/tests/test_base/test_base_common.py b/tests/test_base/test_base_common.py index 8eae1ba0..ead951c8 100644 --- a/tests/test_base/test_base_common.py +++ b/tests/test_base/test_base_common.py @@ -27,18 +27,19 @@ from xml.etree import ElementTree as ET import pytest -from PyQt5.QtCore import QUrl -from PyQt5.QtGui import QColor, QDesktopServices, QFontDatabase +from PyQt5.QtCore import QMimeData, QUrl +from PyQt5.QtGui import QColor, QDesktopServices, QFont, QFontDatabase, QFontInfo from novelwriter.common import ( NWConfigParser, checkBool, checkFloat, checkInt, checkIntTuple, checkPath, - checkString, checkStringNone, checkUuid, compact, cssCol, describeFont, - elide, firstFloat, formatFileFilter, formatInt, formatTime, - formatTimeStamp, formatVersion, fuzzyTime, getFileSize, hexToInt, isHandle, - isItemClass, isItemLayout, isItemType, isListInstance, isTitleTag, - jsonEncode, makeFileNameSafe, minmax, numberToRoman, openExternalPath, - readTextFile, simplified, transferCase, uniqueCompact, xmlElement, - xmlIndent, xmlSubElem, yesNo + checkString, checkStringNone, checkUuid, compact, cssCol, + decodeMimeHandles, describeFont, elide, encodeMimeHandles, firstFloat, + fontMatcher, formatFileFilter, formatInt, formatTime, formatTimeStamp, + formatVersion, fuzzyTime, getFileSize, hexToInt, isHandle, isItemClass, + isItemLayout, isItemType, isListInstance, isTitleTag, jsonEncode, + makeFileNameSafe, minmax, numberToRoman, openExternalPath, readTextFile, + simplified, transferCase, uniqueCompact, xmlElement, xmlIndent, xmlSubElem, + yesNo ) from tests.mocked import causeOSError @@ -528,6 +529,34 @@ def testBaseCommon_describeFont(): assert describeFont(None) == "Error" # type: ignore +@pytest.mark.base +def testBaseCommon_fontMatcher(monkeypatch): + """Test the fontMatcher function.""" + # Nonsense font is just returned + nonsense = QFont("nonesense", 10) + assert fontMatcher(nonsense) is nonsense + + # General font + fontDB = QFontDatabase() + if len(fontDB.families()) > 1: + fontOne = QFont(fontDB.families()[0]) + fontTwo = QFont(fontDB.families()[1]) + check = QFont(fontOne) + check.setFamily(fontTwo.family()) + with monkeypatch.context() as mp: + mp.setattr(QFontInfo, "family", lambda *a: "nonesense") + assert fontMatcher(check).family() == fontTwo.family() + + +@pytest.mark.base +def testBaseCommon_encodeDecodeMimeHandles(monkeypatch): + """Test the encodeMimeHandles and decodeMimeHandles functions.""" + handles = ["0123456789abc", "123456789abcd", "23456789abcde"] + mimeData = QMimeData() + encodeMimeHandles(mimeData, handles) + assert decodeMimeHandles(mimeData) == handles + + @pytest.mark.base def testBaseCommon_jsonEncode(): """Test the jsonEncode function.""" diff --git a/tests/test_base/test_base_error.py b/tests/test_base/test_base_error.py index 05aff792..266243f6 100644 --- a/tests/test_base/test_base_error.py +++ b/tests/test_base/test_base_error.py @@ -101,7 +101,7 @@ def testBaseError_Handler(qtbot, monkeypatch, nwGUI): with monkeypatch.context() as mp: mp.setattr(NWErrorMessage, "exec", lambda *a: None) mp.setattr("PyQt5.QtWidgets.QApplication.exit", lambda *a: None) - mp.setattr(nwGUI, "closeMain", causeException) + mp.setattr("novelwriter.guimain.GuiMain.closeMain", causeException) exceptionHandler(Exception, "Error Message", None) # type: ignore nwGUI.closeMain() diff --git a/tests/test_formats/test_fmt_tokenizer.py b/tests/test_formats/test_fmt_tokenizer.py index bab57bfc..cbfdb0ae 100644 --- a/tests/test_formats/test_fmt_tokenizer.py +++ b/tests/test_formats/test_fmt_tokenizer.py @@ -110,7 +110,7 @@ def testFmtToken_Setters(mockGUI): tokens.setSceneFormat(f"S: {nwHeadFmt.TITLE}", True) tokens.setHardSceneFormat(f"H: {nwHeadFmt.TITLE}", True) tokens.setSectionFormat(f"X: {nwHeadFmt.TITLE}", True) - tokens.setFont(QFont("Monospace", 10)) + tokens.setTextFont(QFont("Monospace", 10)) tokens.setLineHeight(2.0) tokens.setBlockIndent(6.0) tokens.setJustify(True) diff --git a/tests/test_formats/test_fmt_toqdoc.py b/tests/test_formats/test_fmt_toqdoc.py index 74387653..e1b361fc 100644 --- a/tests/test_formats/test_fmt_toqdoc.py +++ b/tests/test_formats/test_fmt_toqdoc.py @@ -22,7 +22,7 @@ from __future__ import annotations import pytest -from PyQt5.QtGui import QTextBlock, QTextCharFormat, QTextCursor +from PyQt5.QtGui import QFont, QTextBlock, QTextCharFormat, QTextCursor from novelwriter import CONFIG from novelwriter.constants import nwUnicode @@ -71,7 +71,7 @@ def testFmtToQTextDocument_ConvertHeaders(mockGUI): assert bFmt.topMargin() == doc._mHead[BlockTyp.TITLE][0] assert bFmt.bottomMargin() == doc._mHead[BlockTyp.TITLE][1] cFmt = charFmtInBlock(block, 1) - assert cFmt.fontWeight() == doc._bold + assert cFmt.fontWeight() == QFont.Weight.Bold assert cFmt.fontPointSize() == doc._sHead[BlockTyp.TITLE] assert cFmt.foreground().color() == THEME.text @@ -82,7 +82,7 @@ def testFmtToQTextDocument_ConvertHeaders(mockGUI): assert bFmt.topMargin() == doc._mHead[BlockTyp.HEAD1][0] assert bFmt.bottomMargin() == doc._mHead[BlockTyp.HEAD1][1] cFmt = charFmtInBlock(block, 1) - assert cFmt.fontWeight() == doc._bold + assert cFmt.fontWeight() == QFont.Weight.Bold assert cFmt.fontPointSize() == doc._sHead[BlockTyp.HEAD1] assert cFmt.foreground().color() == THEME.head @@ -93,7 +93,7 @@ def testFmtToQTextDocument_ConvertHeaders(mockGUI): assert bFmt.topMargin() == doc._mHead[BlockTyp.HEAD2][0] assert bFmt.bottomMargin() == doc._mHead[BlockTyp.HEAD2][1] cFmt = charFmtInBlock(block, 1) - assert cFmt.fontWeight() == doc._bold + assert cFmt.fontWeight() == QFont.Weight.Bold assert cFmt.fontPointSize() == doc._sHead[BlockTyp.HEAD2] assert cFmt.foreground().color() == THEME.head @@ -104,7 +104,7 @@ def testFmtToQTextDocument_ConvertHeaders(mockGUI): assert bFmt.topMargin() == doc._mHead[BlockTyp.HEAD3][0] assert bFmt.bottomMargin() == doc._mHead[BlockTyp.HEAD3][1] cFmt = charFmtInBlock(block, 1) - assert cFmt.fontWeight() == doc._bold + assert cFmt.fontWeight() == QFont.Weight.Bold assert cFmt.fontPointSize() == doc._sHead[BlockTyp.HEAD3] assert cFmt.foreground().color() == THEME.head @@ -115,7 +115,7 @@ def testFmtToQTextDocument_ConvertHeaders(mockGUI): assert bFmt.topMargin() == doc._mHead[BlockTyp.HEAD4][0] assert bFmt.bottomMargin() == doc._mHead[BlockTyp.HEAD4][1] cFmt = charFmtInBlock(block, 1) - assert cFmt.fontWeight() == doc._bold + assert cFmt.fontWeight() == QFont.Weight.Bold assert cFmt.fontPointSize() == doc._sHead[BlockTyp.HEAD4] assert cFmt.foreground().color() == THEME.head @@ -478,11 +478,11 @@ def testFmtToQTextDocument_TextCharFormats(mockGUI): block = doc.document.findBlockByNumber(1) assert block.text() == "With bold text" cFmt = charFmtInBlock(block, 1) - assert cFmt.fontWeight() == doc._normal + assert cFmt.fontWeight() == doc._dWeight cFmt = charFmtInBlock(block, 6) - assert cFmt.fontWeight() == doc._bold + assert cFmt.fontWeight() == QFont.Weight.Bold cFmt = charFmtInBlock(block, 10) - assert cFmt.fontWeight() == doc._normal + assert cFmt.fontWeight() == doc._dWeight # 2: Italic block = doc.document.findBlockByNumber(2) diff --git a/tests/test_tools/test_tools_manuscript.py b/tests/test_tools/test_tools_manuscript.py index 6d94477a..7aa744c2 100644 --- a/tests/test_tools/test_tools_manuscript.py +++ b/tests/test_tools/test_tools_manuscript.py @@ -48,7 +48,7 @@ def testToolManuscript_Init(monkeypatch, qtbot, nwGUI, projPath, mockRnd): buildTestProject(nwGUI, projPath) nwGUI.openProject(projPath) SHARED.project.storage.getDocument(C.hChapterDoc).writeDocument("## A Chapter\n\n\t\tHi") - allText = "New Novel\nBy Jane Doe\n\nNew Page\nA Chapter\n\t\tHi" + allText = "New Novel\nBy Jane Doe\nNew Page\n\nA Chapter\n\t\tHi" nwGUI.mainMenu.aBuildManuscript.activate(QAction.ActionEvent.Trigger) qtbot.waitUntil(lambda: SHARED.findTopLevelWidget(GuiManuscript) is not None, timeout=1000)