diff --git a/novelwriter/dialogs/about.py b/novelwriter/dialogs/about.py
index 8317a30d..da6ba884 100644
--- a/novelwriter/dialogs/about.py
+++ b/novelwriter/dialogs/about.py
@@ -27,20 +27,20 @@ import logging
from PyQt5.QtGui import QCloseEvent, QColor
from PyQt5.QtWidgets import (
- QDialog, QDialogButtonBox, QHBoxLayout, QLabel, QTextBrowser, QVBoxLayout,
- QWidget
+ QDialogButtonBox, QHBoxLayout, QLabel, QTextBrowser, QVBoxLayout, QWidget
)
from novelwriter import CONFIG, SHARED
from novelwriter.common import cssCol, readTextFile
from novelwriter.extensions.configlayout import NColourLabel
+from novelwriter.extensions.modified import NNonBlockingDialog
from novelwriter.extensions.versioninfo import VersionInfoWidget
from novelwriter.types import QtAlignRightTop, QtDialogClose
logger = logging.getLogger(__name__)
-class GuiAbout(QDialog):
+class GuiAbout(NNonBlockingDialog):
def __init__(self, parent: QWidget) -> None:
super().__init__(parent=parent)
diff --git a/novelwriter/extensions/modified.py b/novelwriter/extensions/modified.py
index b83a2662..8f185b24 100644
--- a/novelwriter/extensions/modified.py
+++ b/novelwriter/extensions/modified.py
@@ -6,6 +6,8 @@ File History:
Created: 2024-02-01 [2.3b1] NComboBox
Created: 2024-02-01 [2.3b1] NSpinBox
Created: 2024-02-01 [2.3b1] NDoubleSpinBox
+Created: 2024-05-01 [2.5b1] NToolDialog
+Created: 2024-05-01 [2.5b1] NNonBlockingDialog
This file is a part of novelWriter
Copyright 2018–2024, Veronica Berglyd Olsen
@@ -29,9 +31,52 @@ from enum import Enum
from PyQt5.QtCore import QSize, Qt
from PyQt5.QtGui import QWheelEvent
-from PyQt5.QtWidgets import QComboBox, QDoubleSpinBox, QSpinBox, QToolButton, QWidget
+from PyQt5.QtWidgets import (
+ QApplication, QComboBox, QDialog, QDoubleSpinBox, QSpinBox, QToolButton,
+ QWidget
+)
-from novelwriter import SHARED
+from novelwriter import CONFIG, SHARED
+
+
+class NToolDialog(QDialog):
+
+ def __init__(self, parent: QWidget | None = None) -> None:
+ super().__init__(parent=parent)
+ self.setModal(False)
+ if CONFIG.osDarwin:
+ self.setWindowFlag(Qt.WindowType.Tool)
+ return
+
+ def activateDialog(self) -> None:
+ """Helper function to activate dialog on various systems."""
+ self.show()
+ if CONFIG.osWindows:
+ self.activateWindow()
+ self.raise_()
+ QApplication.processEvents()
+ return
+
+# END Class NToolDialog
+
+
+class NNonBlockingDialog(QDialog):
+
+ def __init__(self, parent: QWidget | None = None) -> None:
+ super().__init__(parent=parent)
+ self.setModal(True)
+ return
+
+ def activateDialog(self) -> None:
+ """Helper function to activate dialog on various systems."""
+ self.show()
+ if CONFIG.osWindows:
+ self.activateWindow()
+ self.raise_()
+ QApplication.processEvents()
+ return
+
+# END Class NNonBlockingDialog
class NComboBox(QComboBox):
diff --git a/novelwriter/guimain.py b/novelwriter/guimain.py
index 105d2652..afe7966d 100644
--- a/novelwriter/guimain.py
+++ b/novelwriter/guimain.py
@@ -23,18 +23,18 @@ along with this program. If not, see .
"""
from __future__ import annotations
-import sys
import logging
+import sys
-from time import time
-from pathlib import Path
from datetime import datetime
+from pathlib import Path
+from time import time
from PyQt5.QtCore import Qt, QTimer, pyqtSlot
from PyQt5.QtGui import QCloseEvent, QCursor, QIcon
from PyQt5.QtWidgets import (
- QApplication, QFileDialog, QHBoxLayout, QMainWindow, QMessageBox, QShortcut, QSplitter,
- QStackedWidget, QVBoxLayout, QWidget
+ QApplication, QFileDialog, QHBoxLayout, QMainWindow, QMessageBox,
+ QShortcut, QSplitter, QStackedWidget, QVBoxLayout, QWidget
)
from novelwriter import CONFIG, SHARED, __hexversion__, __version__
@@ -44,7 +44,7 @@ from novelwriter.dialogs.about import GuiAbout
from novelwriter.dialogs.preferences import GuiPreferences
from novelwriter.dialogs.projectsettings import GuiProjectSettings
from novelwriter.dialogs.wordlist import GuiWordList
-from novelwriter.enum import nwDocAction, nwDocInsert, nwDocMode, nwItemType, nwWidget, nwView
+from novelwriter.enum import nwDocAction, nwDocInsert, nwDocMode, nwItemType, nwView, nwWidget
from novelwriter.gui.doceditor import GuiDocEditor
from novelwriter.gui.docviewer import GuiDocViewer
from novelwriter.gui.docviewerpanel import GuiDocViewerPanel
@@ -794,10 +794,7 @@ class GuiMain(QMainWindow):
"""Open the novel details dialog."""
if SHARED.hasProject:
dialog = GuiNovelDetails(self)
- dialog.setModal(True)
- dialog.show()
- dialog.raise_()
- QApplication.processEvents()
+ dialog.activateDialog()
dialog.updateValues()
return
@@ -807,10 +804,7 @@ class GuiMain(QMainWindow):
if SHARED.hasProject:
if (dialog := SHARED.findTopLevelWidget(GuiManuscript)) is None:
dialog = GuiManuscript(self)
- dialog.setModal(False)
- dialog.show()
- dialog.raise_()
- QApplication.processEvents()
+ dialog.activateDialog()
dialog.loadContent()
return
@@ -829,10 +823,7 @@ class GuiMain(QMainWindow):
if SHARED.hasProject:
if (dialog := SHARED.findTopLevelWidget(GuiWritingStats)) is None:
dialog = GuiWritingStats(self)
- dialog.setModal(False)
- dialog.show()
- dialog.raise_()
- QApplication.processEvents()
+ dialog.activateDialog()
dialog.populateGUI()
return
@@ -840,10 +831,7 @@ class GuiMain(QMainWindow):
def showAboutNWDialog(self) -> None:
"""Show the novelWriter about dialog."""
dialog = GuiAbout(self)
- dialog.setModal(True)
- dialog.show()
- dialog.raise_()
- QApplication.processEvents()
+ dialog.activateDialog()
dialog.populateGUI()
return
@@ -858,10 +846,7 @@ class GuiMain(QMainWindow):
def showDictionariesDialog(self) -> None:
"""Show the download dictionaries dialog."""
dialog = GuiDictionaries(self)
- dialog.setModal(True)
- dialog.show()
- dialog.raise_()
- QApplication.processEvents()
+ dialog.activateDialog()
if not dialog.initDialog():
dialog.close()
SHARED.error(self.tr("Could not initialise the dialog."))
diff --git a/novelwriter/tools/dictionaries.py b/novelwriter/tools/dictionaries.py
index a2607e63..982ebcc9 100644
--- a/novelwriter/tools/dictionaries.py
+++ b/novelwriter/tools/dictionaries.py
@@ -31,20 +31,20 @@ from zipfile import ZipFile
from PyQt5.QtCore import pyqtSlot
from PyQt5.QtGui import QCloseEvent, QTextCursor
from PyQt5.QtWidgets import (
- QApplication, QDialog, QDialogButtonBox, QFileDialog, QFrame, QHBoxLayout,
- QLabel, QLineEdit, QPlainTextEdit, QPushButton, QVBoxLayout, QWidget
+ QApplication, QDialogButtonBox, QFileDialog, QFrame, QHBoxLayout, QLabel,
+ QLineEdit, QPlainTextEdit, QPushButton, QVBoxLayout, QWidget
)
from novelwriter import CONFIG, SHARED
-from novelwriter.common import formatFileFilter, openExternalPath, formatInt, getFileSize
+from novelwriter.common import formatFileFilter, formatInt, getFileSize, openExternalPath
from novelwriter.error import formatException
-from novelwriter.extensions.modified import NIconToolButton
+from novelwriter.extensions.modified import NIconToolButton, NNonBlockingDialog
from novelwriter.types import QtDialogClose
logger = logging.getLogger(__name__)
-class GuiDictionaries(QDialog):
+class GuiDictionaries(NNonBlockingDialog):
def __init__(self, parent: QWidget) -> None:
super().__init__(parent=parent)
diff --git a/novelwriter/tools/manuscript.py b/novelwriter/tools/manuscript.py
index f84dfa1a..6bdbdeae 100644
--- a/novelwriter/tools/manuscript.py
+++ b/novelwriter/tools/manuscript.py
@@ -33,8 +33,8 @@ from PyQt5.QtCore import Qt, QTimer, QUrl, pyqtSignal, pyqtSlot
from PyQt5.QtGui import QCloseEvent, QColor, QCursor, QFont, QPalette, QResizeEvent
from PyQt5.QtPrintSupport import QPrinter, QPrintPreviewDialog
from PyQt5.QtWidgets import (
- QAbstractItemView, QApplication, QDialog, QFormLayout, QGridLayout,
- QHBoxLayout, QLabel, QListWidget, QListWidgetItem, QPushButton, QSplitter,
+ QAbstractItemView, QApplication, QFormLayout, QGridLayout, QHBoxLayout,
+ QLabel, QListWidget, QListWidgetItem, QPushButton, QSplitter,
QStackedWidget, QTabWidget, QTextBrowser, QTreeWidget, QTreeWidgetItem,
QVBoxLayout, QWidget
)
@@ -47,7 +47,7 @@ from novelwriter.core.tohtml import ToHtml
from novelwriter.core.tokenizer import HeadingFormatter
from novelwriter.error import logException
from novelwriter.extensions.circularprogress import NProgressCircle
-from novelwriter.extensions.modified import NIconToggleButton, NIconToolButton
+from novelwriter.extensions.modified import NIconToggleButton, NIconToolButton, NToolDialog
from novelwriter.gui.theme import STYLES_FLAT_TABS, STYLES_MIN_TOOLBUTTON
from novelwriter.tools.manusbuild import GuiManuscriptBuild
from novelwriter.tools.manussettings import GuiBuildSettings
@@ -59,7 +59,7 @@ from novelwriter.types import (
logger = logging.getLogger(__name__)
-class GuiManuscript(QDialog):
+class GuiManuscript(NToolDialog):
"""GUI Tools: Manuscript Tool
The dialog displays all the users build definitions, a preview panel
@@ -74,8 +74,6 @@ class GuiManuscript(QDialog):
logger.debug("Create: GuiManuscript")
self.setObjectName("GuiManuscript")
- if CONFIG.osDarwin:
- self.setWindowFlag(Qt.WindowType.Tool)
self._builds = BuildCollection(SHARED.project)
self._buildMap: dict[str, QListWidgetItem] = {}
@@ -466,17 +464,13 @@ class GuiManuscript(QDialog):
if isinstance(obj, GuiBuildSettings):
if obj.buildID == build.buildID:
logger.debug("Found instance of GuiBuildSettings")
- obj.show()
- obj.raise_()
+ obj.activateDialog()
return
- dlgSettings = GuiBuildSettings(SHARED.mainGui, build)
- dlgSettings.setModal(False)
- dlgSettings.show()
- dlgSettings.raise_()
- QApplication.processEvents()
- dlgSettings.loadContent()
- dlgSettings.newSettingsReady.connect(self._processNewSettings)
+ dialog = GuiBuildSettings(SHARED.mainGui, build)
+ dialog.activateDialog()
+ dialog.loadContent()
+ dialog.newSettingsReady.connect(self._processNewSettings)
return
diff --git a/novelwriter/tools/manussettings.py b/novelwriter/tools/manussettings.py
index cd9ac72e..e138b592 100644
--- a/novelwriter/tools/manussettings.py
+++ b/novelwriter/tools/manussettings.py
@@ -25,11 +25,11 @@ from __future__ import annotations
import logging
-from PyQt5.QtCore import QEvent, Qt, pyqtSignal, pyqtSlot
+from PyQt5.QtCore import QEvent, pyqtSignal, pyqtSlot
from PyQt5.QtGui import QFont, QIcon, QSyntaxHighlighter, QTextCharFormat, QTextDocument
from PyQt5.QtWidgets import (
- QAbstractButton, QAbstractItemView, QDialog, QDialogButtonBox, QFontDialog,
- QFrame, QGridLayout, QHBoxLayout, QHeaderView, QLabel, QLineEdit, QMenu,
+ QAbstractButton, QAbstractItemView, QDialogButtonBox, QFontDialog, QFrame,
+ QGridLayout, QHBoxLayout, QHeaderView, QLabel, QLineEdit, QMenu,
QPlainTextEdit, QPushButton, QSplitter, QStackedWidget, QTreeWidget,
QTreeWidgetItem, QVBoxLayout, QWidget
)
@@ -40,7 +40,9 @@ from novelwriter.core.buildsettings import BuildSettings, FilterMode
from novelwriter.extensions.configlayout import (
NColourLabel, NFixedPage, NScrollableForm, NScrollablePage
)
-from novelwriter.extensions.modified import NComboBox, NDoubleSpinBox, NIconToolButton, NSpinBox
+from novelwriter.extensions.modified import (
+ NComboBox, NDoubleSpinBox, NIconToolButton, NSpinBox, NToolDialog
+)
from novelwriter.extensions.pagedsidebar import NPagedSideBar
from novelwriter.extensions.switch import NSwitch
from novelwriter.extensions.switchbox import NSwitchBox
@@ -52,7 +54,7 @@ from novelwriter.types import (
logger = logging.getLogger(__name__)
-class GuiBuildSettings(QDialog):
+class GuiBuildSettings(NToolDialog):
"""GUI Tools: Manuscript Build Settings Dialog
The main tool for configuring manuscript builds. It's a GUI tool for
@@ -72,8 +74,6 @@ class GuiBuildSettings(QDialog):
logger.debug("Create: GuiBuildSettings")
self.setObjectName("GuiBuildSettings")
- if CONFIG.osDarwin:
- self.setWindowFlag(Qt.WindowType.Tool)
self._build = build
diff --git a/novelwriter/tools/noveldetails.py b/novelwriter/tools/noveldetails.py
index 08755425..91d95651 100644
--- a/novelwriter/tools/noveldetails.py
+++ b/novelwriter/tools/noveldetails.py
@@ -23,21 +23,22 @@ along with this program. If not, see .
"""
from __future__ import annotations
-import math
import logging
+import math
from PyQt5.QtCore import pyqtSlot
from PyQt5.QtGui import QCloseEvent
from PyQt5.QtWidgets import (
- QAbstractItemView, QDialog, QDialogButtonBox, QFormLayout, QGridLayout,
- QHBoxLayout, QLabel, QSpinBox, QStackedWidget, QTreeWidget,
- QTreeWidgetItem, QVBoxLayout, QWidget
+ QAbstractItemView, QDialogButtonBox, QFormLayout, QGridLayout, QHBoxLayout,
+ QLabel, QSpinBox, QStackedWidget, QTreeWidget, QTreeWidgetItem,
+ QVBoxLayout, QWidget
)
from novelwriter import CONFIG, SHARED
from novelwriter.common import formatTime, numberToRoman
from novelwriter.constants import nwUnicode
from novelwriter.extensions.configlayout import NColourLabel, NFixedPage, NScrollablePage
+from novelwriter.extensions.modified import NNonBlockingDialog
from novelwriter.extensions.novelselector import NovelSelector
from novelwriter.extensions.pagedsidebar import NPagedSideBar
from novelwriter.extensions.switch import NSwitch
@@ -46,7 +47,7 @@ from novelwriter.types import QtAlignRight, QtDecoration, QtDialogClose
logger = logging.getLogger(__name__)
-class GuiNovelDetails(QDialog):
+class GuiNovelDetails(NNonBlockingDialog):
PAGE_OVERVIEW = 1
PAGE_CONTENTS = 2
diff --git a/novelwriter/tools/writingstats.py b/novelwriter/tools/writingstats.py
index 1a12e40f..ad0a5508 100644
--- a/novelwriter/tools/writingstats.py
+++ b/novelwriter/tools/writingstats.py
@@ -32,15 +32,16 @@ from typing import TYPE_CHECKING
from PyQt5.QtCore import Qt, pyqtSlot
from PyQt5.QtGui import QCloseEvent, QCursor, QPixmap
from PyQt5.QtWidgets import (
- QAction, QApplication, QDialog, QDialogButtonBox, QFileDialog, QGridLayout,
+ QAction, QApplication, QDialogButtonBox, QFileDialog, QGridLayout,
QGroupBox, QHBoxLayout, QLabel, QMenu, QSpinBox, QTreeWidget,
QTreeWidgetItem
)
from novelwriter import CONFIG, SHARED
-from novelwriter.common import formatTime, checkInt, checkIntTuple, minmax
+from novelwriter.common import checkInt, checkIntTuple, formatTime, minmax
from novelwriter.constants import nwConst
from novelwriter.error import formatException
+from novelwriter.extensions.modified import NToolDialog
from novelwriter.extensions.switch import NSwitch
from novelwriter.types import (
QtAlignLeftMiddle, QtAlignRight, QtAlignRightMiddle, QtDecoration,
@@ -53,7 +54,7 @@ if TYPE_CHECKING: # pragma: no cover
logger = logging.getLogger(__name__)
-class GuiWritingStats(QDialog):
+class GuiWritingStats(NToolDialog):
"""GUI Tools: Writing Statistics
Displays data from the NWSessionLog object.
@@ -73,8 +74,6 @@ class GuiWritingStats(QDialog):
logger.debug("Create: GuiWritingStats")
self.setObjectName("GuiWritingStats")
- if CONFIG.osDarwin:
- self.setWindowFlag(Qt.WindowType.Tool)
self.logData = []
self.filterData = []
diff --git a/tests/test_tools/test_tools_manuscript.py b/tests/test_tools/test_tools_manuscript.py
index 5e80767f..b134b5c2 100644
--- a/tests/test_tools/test_tools_manuscript.py
+++ b/tests/test_tools/test_tools_manuscript.py
@@ -20,18 +20,18 @@ along with this program. If not, see .
"""
from __future__ import annotations
-import pytest
import sys
from pathlib import Path
-from pytestqt.qtbot import QtBot
+
+import pytest
from mocked import causeOSError
-from tools import C, buildTestProject
-
from PyQt5.QtCore import pyqtSlot
from PyQt5.QtPrintSupport import QPrintPreviewDialog
from PyQt5.QtWidgets import QAction, QListWidgetItem
+from pytestqt.qtbot import QtBot
+from tools import C, buildTestProject
from novelwriter import CONFIG, SHARED
from novelwriter.constants import nwHeadFmt
@@ -127,8 +127,10 @@ def testManuscript_Builds(qtbot: QtBot, nwGUI: GuiMain, projPath: Path):
# Edit the new build
manus.buildList.clearSelection()
manus.buildList.setCurrentRow(0)
- manus.tbEdit.click()
+ with qtbot.waitSignal(manus.tbEdit.clicked, timeout=5000):
+ manus.tbEdit.click()
+ manus._editSelectedBuild()
bSettings = SHARED.findTopLevelWidget(GuiBuildSettings)
assert isinstance(bSettings, GuiBuildSettings)
build = None
@@ -147,7 +149,6 @@ def testManuscript_Builds(qtbot: QtBot, nwGUI: GuiMain, projPath: Path):
# May have been garbage collected, but if it isn't, it should be hidden
assert bSettings.isVisible() is False
- # Finish
# qtbot.stop()
# END Test testManuscript_Builds