Merge branch 'main' into release_2.2.1
This commit is contained in:
+14
-14
@@ -46,9 +46,9 @@ from novelwriter.constants import nwConst, nwUnicode
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
# =============================================================================================== #
|
##
|
||||||
# Checker Functions
|
# Checker Functions
|
||||||
# =============================================================================================== #
|
##
|
||||||
|
|
||||||
def checkStringNone(value: Any, default: str | None) -> str | None:
|
def checkStringNone(value: Any, default: str | None) -> str | None:
|
||||||
"""Check if a variable is a string or a None."""
|
"""Check if a variable is a string or a None."""
|
||||||
@@ -131,9 +131,9 @@ def checkPath(value: Any, default: Path) -> Path:
|
|||||||
return default
|
return default
|
||||||
|
|
||||||
|
|
||||||
# =============================================================================================== #
|
##
|
||||||
# Validator Functions
|
# Validator Functions
|
||||||
# =============================================================================================== #
|
##
|
||||||
|
|
||||||
def isHandle(value: Any) -> bool:
|
def isHandle(value: Any) -> bool:
|
||||||
"""Check if a string is a valid novelWriter handle.
|
"""Check if a string is a valid novelWriter handle.
|
||||||
@@ -204,9 +204,9 @@ def checkIntTuple(value: int, valid: tuple | list | set, default: int) -> int:
|
|||||||
return default
|
return default
|
||||||
|
|
||||||
|
|
||||||
# =============================================================================================== #
|
##
|
||||||
# Formatting Functions
|
# Formatting Functions
|
||||||
# =============================================================================================== #
|
##
|
||||||
|
|
||||||
def formatInt(value: int) -> str:
|
def formatInt(value: int) -> str:
|
||||||
"""Formats an integer with k, M, G etc."""
|
"""Formats an integer with k, M, G etc."""
|
||||||
@@ -250,9 +250,9 @@ def formatTime(t: int) -> str:
|
|||||||
return "ERROR"
|
return "ERROR"
|
||||||
|
|
||||||
|
|
||||||
# =============================================================================================== #
|
##
|
||||||
# String Functions
|
# String Functions
|
||||||
# =============================================================================================== #
|
##
|
||||||
|
|
||||||
def simplified(text: str) -> str:
|
def simplified(text: str) -> str:
|
||||||
"""Take a string and strip leading and trailing whitespaces, and
|
"""Take a string and strip leading and trailing whitespaces, and
|
||||||
@@ -371,9 +371,9 @@ def numberToRoman(value: int, toLower: bool = False) -> str:
|
|||||||
return roman.lower() if toLower else roman
|
return roman.lower() if toLower else roman
|
||||||
|
|
||||||
|
|
||||||
# =============================================================================================== #
|
##
|
||||||
# Encoder Functions
|
# Encoder Functions
|
||||||
# =============================================================================================== #
|
##
|
||||||
|
|
||||||
def jsonEncode(data: dict | list | tuple, n: int = 0, nmax: int = 0) -> str:
|
def jsonEncode(data: dict | list | tuple, n: int = 0, nmax: int = 0) -> str:
|
||||||
"""Encode a dictionary, list or tuple as a json object or array, and
|
"""Encode a dictionary, list or tuple as a json object or array, and
|
||||||
@@ -463,9 +463,9 @@ def xmlIndent(tree: ET.Element | ET.ElementTree) -> None:
|
|||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
# =============================================================================================== #
|
##
|
||||||
# File and File System Functions
|
# File and File System Functions
|
||||||
# =============================================================================================== #
|
##
|
||||||
|
|
||||||
def readTextFile(path: str | Path) -> str:
|
def readTextFile(path: str | Path) -> str:
|
||||||
"""Read the content of a text file in a robust manner."""
|
"""Read the content of a text file in a robust manner."""
|
||||||
@@ -507,9 +507,9 @@ def openExternalPath(path: Path) -> bool:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
# =============================================================================================== #
|
##
|
||||||
# Classes
|
# Classes
|
||||||
# =============================================================================================== #
|
##
|
||||||
|
|
||||||
class NWConfigParser(ConfigParser):
|
class NWConfigParser(ConfigParser):
|
||||||
"""Common: Adapted Config Parser
|
"""Common: Adapted Config Parser
|
||||||
|
|||||||
@@ -899,7 +899,8 @@ class GuiMain(QMainWindow):
|
|||||||
def showBuildManuscriptDialog(self) -> None:
|
def showBuildManuscriptDialog(self) -> None:
|
||||||
"""Open the build manuscript dialog."""
|
"""Open the build manuscript dialog."""
|
||||||
if SHARED.hasProject:
|
if SHARED.hasProject:
|
||||||
dialog = GuiManuscript(self)
|
if (dialog := SHARED.findTopLevelWidget(GuiManuscript)) is None:
|
||||||
|
dialog = GuiManuscript(self)
|
||||||
dialog.setModal(False)
|
dialog.setModal(False)
|
||||||
dialog.show()
|
dialog.show()
|
||||||
dialog.raise_()
|
dialog.raise_()
|
||||||
@@ -920,7 +921,8 @@ class GuiMain(QMainWindow):
|
|||||||
def showWritingStatsDialog(self) -> None:
|
def showWritingStatsDialog(self) -> None:
|
||||||
"""Open the session stats dialog."""
|
"""Open the session stats dialog."""
|
||||||
if SHARED.hasProject:
|
if SHARED.hasProject:
|
||||||
dialog = GuiWritingStats(self)
|
if (dialog := SHARED.findTopLevelWidget(GuiWritingStats)) is None:
|
||||||
|
dialog = GuiWritingStats(self)
|
||||||
dialog.setModal(False)
|
dialog.setModal(False)
|
||||||
dialog.show()
|
dialog.show()
|
||||||
dialog.raise_()
|
dialog.raise_()
|
||||||
|
|||||||
+10
-1
@@ -27,7 +27,7 @@ from __future__ import annotations
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from time import time
|
from time import time
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING, TypeVar
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from PyQt5.QtCore import QObject, QRunnable, QThreadPool, pyqtSignal
|
from PyQt5.QtCore import QObject, QRunnable, QThreadPool, pyqtSignal
|
||||||
@@ -42,6 +42,8 @@ if TYPE_CHECKING: # pragma: no cover
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
NWWidget = TypeVar("NWWidget", bound=QWidget)
|
||||||
|
|
||||||
|
|
||||||
class SharedData(QObject):
|
class SharedData(QObject):
|
||||||
|
|
||||||
@@ -215,6 +217,13 @@ class SharedData(QObject):
|
|||||||
QThreadPool.globalInstance().start(runnable, priority=priority)
|
QThreadPool.globalInstance().start(runnable, priority=priority)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def findTopLevelWidget(self, kind: type[NWWidget]) -> NWWidget | None:
|
||||||
|
"""Find a top level widget."""
|
||||||
|
for widget in self.mainGui.children():
|
||||||
|
if isinstance(widget, kind):
|
||||||
|
return widget
|
||||||
|
return None
|
||||||
|
|
||||||
##
|
##
|
||||||
# Signal Proxy
|
# Signal Proxy
|
||||||
##
|
##
|
||||||
|
|||||||
Reference in New Issue
Block a user