Prevent multiple non-modal dialogs of the same kind from being opened
This commit is contained in:
+14
-14
@@ -46,9 +46,9 @@ from novelwriter.constants import nwConst, nwUnicode
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# =============================================================================================== #
|
||||
##
|
||||
# Checker Functions
|
||||
# =============================================================================================== #
|
||||
##
|
||||
|
||||
def checkStringNone(value: Any, default: str | None) -> str | None:
|
||||
"""Check if a variable is a string or a None."""
|
||||
@@ -131,9 +131,9 @@ def checkPath(value: Any, default: Path) -> Path:
|
||||
return default
|
||||
|
||||
|
||||
# =============================================================================================== #
|
||||
##
|
||||
# Validator Functions
|
||||
# =============================================================================================== #
|
||||
##
|
||||
|
||||
def isHandle(value: Any) -> bool:
|
||||
"""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
|
||||
|
||||
|
||||
# =============================================================================================== #
|
||||
##
|
||||
# Formatting Functions
|
||||
# =============================================================================================== #
|
||||
##
|
||||
|
||||
def formatInt(value: int) -> str:
|
||||
"""Formats an integer with k, M, G etc."""
|
||||
@@ -250,9 +250,9 @@ def formatTime(t: int) -> str:
|
||||
return "ERROR"
|
||||
|
||||
|
||||
# =============================================================================================== #
|
||||
##
|
||||
# String Functions
|
||||
# =============================================================================================== #
|
||||
##
|
||||
|
||||
def simplified(text: str) -> str:
|
||||
"""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
|
||||
|
||||
|
||||
# =============================================================================================== #
|
||||
##
|
||||
# Encoder Functions
|
||||
# =============================================================================================== #
|
||||
##
|
||||
|
||||
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
|
||||
@@ -463,9 +463,9 @@ def xmlIndent(tree: ET.Element | ET.ElementTree) -> None:
|
||||
return
|
||||
|
||||
|
||||
# =============================================================================================== #
|
||||
##
|
||||
# File and File System Functions
|
||||
# =============================================================================================== #
|
||||
##
|
||||
|
||||
def readTextFile(path: str | Path) -> str:
|
||||
"""Read the content of a text file in a robust manner."""
|
||||
@@ -507,9 +507,9 @@ def openExternalPath(path: Path) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
# =============================================================================================== #
|
||||
##
|
||||
# Classes
|
||||
# =============================================================================================== #
|
||||
##
|
||||
|
||||
class NWConfigParser(ConfigParser):
|
||||
"""Common: Adapted Config Parser
|
||||
|
||||
@@ -899,7 +899,8 @@ class GuiMain(QMainWindow):
|
||||
def showBuildManuscriptDialog(self) -> None:
|
||||
"""Open the build manuscript dialog."""
|
||||
if SHARED.hasProject:
|
||||
dialog = GuiManuscript(self)
|
||||
if (dialog := SHARED.findTopLevelWidget(GuiManuscript)) is None:
|
||||
dialog = GuiManuscript(self)
|
||||
dialog.setModal(False)
|
||||
dialog.show()
|
||||
dialog.raise_()
|
||||
@@ -920,7 +921,8 @@ class GuiMain(QMainWindow):
|
||||
def showWritingStatsDialog(self) -> None:
|
||||
"""Open the session stats dialog."""
|
||||
if SHARED.hasProject:
|
||||
dialog = GuiWritingStats(self)
|
||||
if (dialog := SHARED.findTopLevelWidget(GuiWritingStats)) is None:
|
||||
dialog = GuiWritingStats(self)
|
||||
dialog.setModal(False)
|
||||
dialog.show()
|
||||
dialog.raise_()
|
||||
|
||||
+10
-1
@@ -27,7 +27,7 @@ from __future__ import annotations
|
||||
import logging
|
||||
|
||||
from time import time
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, TypeVar
|
||||
from pathlib import Path
|
||||
|
||||
from PyQt5.QtCore import QObject, QRunnable, QThreadPool, pyqtSignal
|
||||
@@ -42,6 +42,8 @@ if TYPE_CHECKING: # pragma: no cover
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
NWWidget = TypeVar("NWWidget", bound=QWidget)
|
||||
|
||||
|
||||
class SharedData(QObject):
|
||||
|
||||
@@ -215,6 +217,13 @@ class SharedData(QObject):
|
||||
QThreadPool.globalInstance().start(runnable, priority=priority)
|
||||
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
|
||||
##
|
||||
|
||||
Reference in New Issue
Block a user