novelWriter 2.3.1__py3-none-any.whl → 2.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {novelWriter-2.3.1.dist-info → novelWriter-2.4.dist-info}/METADATA +5 -6
- {novelWriter-2.3.1.dist-info → novelWriter-2.4.dist-info}/RECORD +114 -107
- novelwriter/__init__.py +17 -10
- novelwriter/assets/i18n/nw_de_DE.qm +0 -0
- novelwriter/assets/i18n/nw_en_US.qm +0 -0
- novelwriter/assets/i18n/nw_es_419.qm +0 -0
- novelwriter/assets/i18n/nw_fr_FR.qm +0 -0
- novelwriter/assets/i18n/nw_it_IT.qm +0 -0
- novelwriter/assets/i18n/nw_ja_JP.qm +0 -0
- novelwriter/assets/i18n/nw_nb_NO.qm +0 -0
- novelwriter/assets/i18n/nw_nl_NL.qm +0 -0
- novelwriter/assets/i18n/nw_pt_BR.qm +0 -0
- novelwriter/assets/i18n/nw_zh_CN.qm +0 -0
- novelwriter/assets/icons/none.svg +4 -0
- novelwriter/assets/icons/typicons_dark/icons.conf +4 -0
- novelwriter/assets/icons/typicons_dark/nw_tb-mark.svg +7 -0
- novelwriter/assets/icons/typicons_dark/typ_refresh-flipped.svg +1 -1
- novelwriter/assets/icons/typicons_dark/typ_refresh.svg +1 -1
- novelwriter/assets/icons/typicons_dark/typ_search-grey.svg +4 -0
- novelwriter/assets/icons/typicons_dark/typ_times.svg +1 -1
- novelwriter/assets/icons/typicons_dark/typ_unfold-hidden.svg +4 -0
- novelwriter/assets/icons/typicons_dark/typ_unfold-visible.svg +4 -0
- novelwriter/assets/icons/typicons_light/icons.conf +4 -0
- novelwriter/assets/icons/typicons_light/nw_tb-mark.svg +7 -0
- novelwriter/assets/icons/typicons_light/typ_refresh-flipped.svg +1 -1
- novelwriter/assets/icons/typicons_light/typ_refresh.svg +1 -1
- novelwriter/assets/icons/typicons_light/typ_search-grey.svg +4 -0
- novelwriter/assets/icons/typicons_light/typ_times.svg +1 -1
- novelwriter/assets/icons/typicons_light/typ_unfold-hidden.svg +4 -0
- novelwriter/assets/icons/typicons_light/typ_unfold-visible.svg +4 -0
- novelwriter/assets/manual.pdf +0 -0
- novelwriter/assets/sample.zip +0 -0
- novelwriter/assets/syntax/default_dark.conf +1 -0
- novelwriter/assets/syntax/default_light.conf +1 -0
- novelwriter/assets/syntax/grey_dark.conf +1 -0
- novelwriter/assets/syntax/grey_light.conf +1 -0
- novelwriter/assets/syntax/light_owl.conf +1 -0
- novelwriter/assets/syntax/night_owl.conf +1 -0
- novelwriter/assets/syntax/solarized_dark.conf +1 -0
- novelwriter/assets/syntax/solarized_light.conf +1 -0
- novelwriter/assets/syntax/tomorrow.conf +1 -0
- novelwriter/assets/syntax/tomorrow_night.conf +1 -0
- novelwriter/assets/syntax/tomorrow_night_blue.conf +1 -0
- novelwriter/assets/syntax/tomorrow_night_bright.conf +1 -0
- novelwriter/assets/syntax/tomorrow_night_eighties.conf +1 -0
- novelwriter/assets/text/credits_en.htm +25 -23
- novelwriter/common.py +12 -4
- novelwriter/config.py +47 -16
- novelwriter/constants.py +5 -6
- novelwriter/core/buildsettings.py +64 -44
- novelwriter/core/coretools.py +97 -13
- novelwriter/core/docbuild.py +74 -7
- novelwriter/core/document.py +24 -3
- novelwriter/core/index.py +31 -112
- novelwriter/core/project.py +10 -15
- novelwriter/core/projectxml.py +1 -1
- novelwriter/core/sessions.py +2 -2
- novelwriter/core/spellcheck.py +3 -3
- novelwriter/core/status.py +6 -5
- novelwriter/core/storage.py +8 -2
- novelwriter/core/tohtml.py +22 -25
- novelwriter/core/tokenizer.py +417 -233
- novelwriter/core/tomd.py +17 -8
- novelwriter/core/toodt.py +386 -351
- novelwriter/core/tree.py +8 -8
- novelwriter/dialogs/about.py +9 -11
- novelwriter/dialogs/docmerge.py +17 -14
- novelwriter/dialogs/docsplit.py +20 -19
- novelwriter/dialogs/editlabel.py +5 -4
- novelwriter/dialogs/preferences.py +32 -40
- novelwriter/dialogs/projectsettings.py +31 -28
- novelwriter/dialogs/quotes.py +10 -9
- novelwriter/dialogs/wordlist.py +17 -14
- novelwriter/enum.py +17 -14
- novelwriter/error.py +14 -12
- novelwriter/extensions/circularprogress.py +12 -8
- novelwriter/extensions/configlayout.py +1 -3
- novelwriter/extensions/modified.py +51 -2
- novelwriter/extensions/pagedsidebar.py +16 -14
- novelwriter/extensions/simpleprogress.py +3 -1
- novelwriter/extensions/statusled.py +3 -1
- novelwriter/extensions/switch.py +10 -9
- novelwriter/extensions/switchbox.py +14 -13
- novelwriter/extensions/versioninfo.py +1 -1
- novelwriter/gui/doceditor.py +433 -496
- novelwriter/gui/dochighlight.py +54 -33
- novelwriter/gui/docviewer.py +162 -175
- novelwriter/gui/docviewerpanel.py +20 -37
- novelwriter/gui/editordocument.py +15 -4
- novelwriter/gui/itemdetails.py +51 -54
- novelwriter/gui/mainmenu.py +37 -16
- novelwriter/gui/noveltree.py +31 -37
- novelwriter/gui/outline.py +120 -98
- novelwriter/gui/projtree.py +61 -67
- novelwriter/gui/search.py +362 -0
- novelwriter/gui/sidebar.py +36 -45
- novelwriter/gui/statusbar.py +14 -14
- novelwriter/gui/theme.py +107 -32
- novelwriter/guimain.py +209 -202
- novelwriter/shared.py +31 -6
- novelwriter/text/counting.py +138 -0
- novelwriter/tools/dictionaries.py +15 -14
- novelwriter/tools/lipsum.py +20 -17
- novelwriter/tools/manusbuild.py +43 -35
- novelwriter/tools/manuscript.py +381 -104
- novelwriter/tools/manussettings.py +262 -125
- novelwriter/tools/noveldetails.py +20 -18
- novelwriter/tools/welcome.py +52 -49
- novelwriter/tools/writingstats.py +61 -55
- novelwriter/types.py +90 -0
- novelwriter/core/__init__.py +0 -3
- novelwriter/dialogs/__init__.py +0 -3
- novelwriter/extensions/__init__.py +0 -3
- novelwriter/gui/__init__.py +0 -3
- novelwriter/tools/__init__.py +0 -3
- {novelWriter-2.3.1.dist-info → novelWriter-2.4.dist-info}/LICENSE.md +0 -0
- {novelWriter-2.3.1.dist-info → novelWriter-2.4.dist-info}/WHEEL +0 -0
- {novelWriter-2.3.1.dist-info → novelWriter-2.4.dist-info}/entry_points.txt +0 -0
- {novelWriter-2.3.1.dist-info → novelWriter-2.4.dist-info}/top_level.txt +0 -0
novelwriter/tools/manuscript.py
CHANGED
@@ -26,29 +26,36 @@ from __future__ import annotations
|
|
26
26
|
import json
|
27
27
|
import logging
|
28
28
|
|
29
|
+
from datetime import datetime
|
29
30
|
from time import time
|
30
31
|
from typing import TYPE_CHECKING
|
31
|
-
from datetime import datetime
|
32
32
|
|
33
|
+
from PyQt5.QtCore import QTimer, QUrl, Qt, pyqtSignal, pyqtSlot
|
33
34
|
from PyQt5.QtGui import QCloseEvent, QColor, QCursor, QFont, QPalette, QResizeEvent
|
34
|
-
from PyQt5.
|
35
|
+
from PyQt5.QtPrintSupport import QPrintPreviewDialog, QPrinter
|
35
36
|
from PyQt5.QtWidgets import (
|
36
|
-
QAbstractItemView,
|
37
|
-
|
38
|
-
|
37
|
+
QAbstractItemView, QApplication, QDialog, QFormLayout, QGridLayout,
|
38
|
+
QHBoxLayout, QLabel, QListWidget, QListWidgetItem, QPushButton,
|
39
|
+
QSizePolicy, QSplitter, QStackedWidget, QTabWidget, QTextBrowser,
|
40
|
+
QTreeWidget, QTreeWidgetItem, QVBoxLayout, QWidget
|
39
41
|
)
|
40
|
-
from PyQt5.QtPrintSupport import QPrintPreviewDialog, QPrinter
|
41
42
|
|
42
43
|
from novelwriter import CONFIG, SHARED
|
43
|
-
from novelwriter.error import logException
|
44
44
|
from novelwriter.common import checkInt, fuzzyTime
|
45
|
-
from novelwriter.core.
|
45
|
+
from novelwriter.core.buildsettings import BuildCollection, BuildSettings
|
46
46
|
from novelwriter.core.docbuild import NWBuildDocument
|
47
|
+
from novelwriter.core.tohtml import ToHtml
|
47
48
|
from novelwriter.core.tokenizer import HeadingFormatter
|
48
|
-
from novelwriter.
|
49
|
+
from novelwriter.error import logException
|
50
|
+
from novelwriter.extensions.circularprogress import NProgressCircle
|
51
|
+
from novelwriter.extensions.modified import NIconToggleButton, NIconToolButton
|
52
|
+
from novelwriter.gui.theme import STYLES_FLAT_TABS, STYLES_MIN_TOOLBUTTON
|
49
53
|
from novelwriter.tools.manusbuild import GuiManuscriptBuild
|
50
54
|
from novelwriter.tools.manussettings import GuiBuildSettings
|
51
|
-
from novelwriter.
|
55
|
+
from novelwriter.types import (
|
56
|
+
QtAlignAbsolute, QtAlignCenter, QtAlignJustify, QtAlignRight, QtAlignTop,
|
57
|
+
QtUserRole
|
58
|
+
)
|
52
59
|
|
53
60
|
if TYPE_CHECKING: # pragma: no cover
|
54
61
|
from novelwriter.guimain import GuiMain
|
@@ -64,7 +71,7 @@ class GuiManuscript(QDialog):
|
|
64
71
|
a document directly to disk.
|
65
72
|
"""
|
66
73
|
|
67
|
-
D_KEY =
|
74
|
+
D_KEY = QtUserRole
|
68
75
|
|
69
76
|
def __init__(self, mainGui: GuiMain) -> None:
|
70
77
|
super().__init__(parent=mainGui)
|
@@ -83,7 +90,7 @@ class GuiManuscript(QDialog):
|
|
83
90
|
self.setMinimumWidth(CONFIG.pxInt(600))
|
84
91
|
self.setMinimumHeight(CONFIG.pxInt(500))
|
85
92
|
|
86
|
-
|
93
|
+
iSz = SHARED.theme.baseIconSize
|
87
94
|
wWin = CONFIG.pxInt(900)
|
88
95
|
hWin = CONFIG.pxInt(600)
|
89
96
|
|
@@ -97,37 +104,27 @@ class GuiManuscript(QDialog):
|
|
97
104
|
# ==============
|
98
105
|
|
99
106
|
qPalette = self.palette()
|
100
|
-
qPalette.setBrush(QPalette.Window, qPalette.base())
|
107
|
+
qPalette.setBrush(QPalette.ColorRole.Window, qPalette.base())
|
101
108
|
self.setPalette(qPalette)
|
102
109
|
|
103
|
-
|
104
|
-
buttonStyle = (
|
105
|
-
"QToolButton {{padding: {0}px; border: none; background: transparent;}} "
|
106
|
-
"QToolButton:hover {{border: none; background: rgba({1},{2},{3},0.2);}}"
|
107
|
-
).format(CONFIG.pxInt(2), fadeCol.red(), fadeCol.green(), fadeCol.blue())
|
110
|
+
buttonStyle = SHARED.theme.getStyleSheet(STYLES_MIN_TOOLBUTTON)
|
108
111
|
|
109
|
-
self.tbAdd =
|
110
|
-
self.tbAdd.setIcon(SHARED.theme.getIcon("add"))
|
111
|
-
self.tbAdd.setIconSize(QSize(iPx, iPx))
|
112
|
+
self.tbAdd = NIconToolButton(self, iSz, "add")
|
112
113
|
self.tbAdd.setToolTip(self.tr("Add New Build"))
|
113
114
|
self.tbAdd.setStyleSheet(buttonStyle)
|
114
115
|
self.tbAdd.clicked.connect(self._createNewBuild)
|
115
116
|
|
116
|
-
self.tbDel =
|
117
|
-
self.tbDel.setIcon(SHARED.theme.getIcon("remove"))
|
118
|
-
self.tbDel.setIconSize(QSize(iPx, iPx))
|
117
|
+
self.tbDel = NIconToolButton(self, iSz, "remove")
|
119
118
|
self.tbDel.setToolTip(self.tr("Delete Selected Build"))
|
120
119
|
self.tbDel.setStyleSheet(buttonStyle)
|
121
120
|
self.tbDel.clicked.connect(self._deleteSelectedBuild)
|
122
121
|
|
123
|
-
self.tbEdit =
|
124
|
-
self.tbEdit.setIcon(SHARED.theme.getIcon("edit"))
|
125
|
-
self.tbEdit.setIconSize(QSize(iPx, iPx))
|
122
|
+
self.tbEdit = NIconToolButton(self, iSz, "edit")
|
126
123
|
self.tbEdit.setToolTip(self.tr("Edit Selected Build"))
|
127
124
|
self.tbEdit.setStyleSheet(buttonStyle)
|
128
125
|
self.tbEdit.clicked.connect(self._editSelectedBuild)
|
129
126
|
|
130
|
-
self.lblBuilds = QLabel("<b>{0}</b>".format(self.tr("Builds")))
|
127
|
+
self.lblBuilds = QLabel("<b>{0}</b>".format(self.tr("Builds")), self)
|
131
128
|
|
132
129
|
self.listToolBox = QHBoxLayout()
|
133
130
|
self.listToolBox.addWidget(self.lblBuilds)
|
@@ -140,21 +137,31 @@ class GuiManuscript(QDialog):
|
|
140
137
|
# Builds
|
141
138
|
# ======
|
142
139
|
|
143
|
-
self.buildList = QListWidget()
|
144
|
-
self.buildList.setIconSize(
|
140
|
+
self.buildList = QListWidget(self)
|
141
|
+
self.buildList.setIconSize(iSz)
|
145
142
|
self.buildList.doubleClicked.connect(self._editSelectedBuild)
|
146
143
|
self.buildList.currentItemChanged.connect(self._updateBuildDetails)
|
147
|
-
self.buildList.setSelectionMode(QAbstractItemView.SingleSelection)
|
148
|
-
self.buildList.setDragDropMode(QAbstractItemView.InternalMove)
|
144
|
+
self.buildList.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)
|
145
|
+
self.buildList.setDragDropMode(QAbstractItemView.DragDropMode.InternalMove)
|
146
|
+
|
147
|
+
# Details Tabs
|
148
|
+
# ============
|
149
149
|
|
150
150
|
self.buildDetails = _DetailsWidget(self)
|
151
151
|
self.buildDetails.setColumnWidth(
|
152
152
|
CONFIG.pxInt(pOptions.getInt("GuiManuscript", "detailsWidth", 100)),
|
153
153
|
)
|
154
154
|
|
155
|
+
self.buildOutline = _OutlineWidget(self)
|
156
|
+
|
157
|
+
self.detailsTabs = QTabWidget(self)
|
158
|
+
self.detailsTabs.addTab(self.buildDetails, self.tr("Details"))
|
159
|
+
self.detailsTabs.addTab(self.buildOutline, self.tr("Outline"))
|
160
|
+
self.detailsTabs.setStyleSheet(SHARED.theme.getStyleSheet(STYLES_FLAT_TABS))
|
161
|
+
|
155
162
|
self.buildSplit = QSplitter(Qt.Orientation.Vertical, self)
|
156
163
|
self.buildSplit.addWidget(self.buildList)
|
157
|
-
self.buildSplit.addWidget(self.
|
164
|
+
self.buildSplit.addWidget(self.detailsTabs)
|
158
165
|
self.buildSplit.setSizes([
|
159
166
|
CONFIG.pxInt(pOptions.getInt("GuiManuscript", "listHeight", 50)),
|
160
167
|
CONFIG.pxInt(pOptions.getInt("GuiManuscript", "detailsHeight", 50)),
|
@@ -163,16 +170,16 @@ class GuiManuscript(QDialog):
|
|
163
170
|
# Process Controls
|
164
171
|
# ================
|
165
172
|
|
166
|
-
self.btnPreview = QPushButton(self.tr("Preview"))
|
173
|
+
self.btnPreview = QPushButton(self.tr("Preview"), self)
|
167
174
|
self.btnPreview.clicked.connect(self._generatePreview)
|
168
175
|
|
169
|
-
self.btnPrint = QPushButton(self.tr("Print"))
|
176
|
+
self.btnPrint = QPushButton(self.tr("Print"), self)
|
170
177
|
self.btnPrint.clicked.connect(self._printDocument)
|
171
178
|
|
172
|
-
self.btnBuild = QPushButton(self.tr("Build"))
|
179
|
+
self.btnBuild = QPushButton(self.tr("Build"), self)
|
173
180
|
self.btnBuild.clicked.connect(self._buildManuscript)
|
174
181
|
|
175
|
-
self.btnClose = QPushButton(self.tr("Close"))
|
182
|
+
self.btnClose = QPushButton(self.tr("Close"), self)
|
176
183
|
self.btnClose.clicked.connect(self.close)
|
177
184
|
|
178
185
|
self.processBox = QGridLayout()
|
@@ -185,6 +192,15 @@ class GuiManuscript(QDialog):
|
|
185
192
|
# ============
|
186
193
|
|
187
194
|
self.docPreview = _PreviewWidget(self)
|
195
|
+
self.docStats = _StatsWidget(self)
|
196
|
+
|
197
|
+
self.docBox = QVBoxLayout()
|
198
|
+
self.docBox.addWidget(self.docPreview, 1)
|
199
|
+
self.docBox.addWidget(self.docStats, 0)
|
200
|
+
self.docBox.setContentsMargins(0, 0, 0, 0)
|
201
|
+
|
202
|
+
self.docWdiget = QWidget(self)
|
203
|
+
self.docWdiget.setLayout(self.docBox)
|
188
204
|
|
189
205
|
self.controlBox = QVBoxLayout()
|
190
206
|
self.controlBox.addLayout(self.listToolBox, 0)
|
@@ -192,12 +208,12 @@ class GuiManuscript(QDialog):
|
|
192
208
|
self.controlBox.addLayout(self.processBox, 0)
|
193
209
|
self.controlBox.setContentsMargins(0, 0, 0, 0)
|
194
210
|
|
195
|
-
self.optsWidget = QWidget()
|
211
|
+
self.optsWidget = QWidget(self)
|
196
212
|
self.optsWidget.setLayout(self.controlBox)
|
197
213
|
|
198
|
-
self.mainSplit = QSplitter()
|
214
|
+
self.mainSplit = QSplitter(self)
|
199
215
|
self.mainSplit.addWidget(self.optsWidget)
|
200
|
-
self.mainSplit.addWidget(self.
|
216
|
+
self.mainSplit.addWidget(self.docWdiget)
|
201
217
|
self.mainSplit.setCollapsible(0, False)
|
202
218
|
self.mainSplit.setCollapsible(1, False)
|
203
219
|
self.mainSplit.setStretchFactor(0, 0)
|
@@ -213,6 +229,9 @@ class GuiManuscript(QDialog):
|
|
213
229
|
self.setLayout(self.outerBox)
|
214
230
|
self.setSizeGripEnabled(True)
|
215
231
|
|
232
|
+
# Signals
|
233
|
+
self.buildOutline.outlineEntryClicked.connect(self.docPreview.navigateTo)
|
234
|
+
|
216
235
|
logger.debug("Ready: GuiManuscript")
|
217
236
|
|
218
237
|
return
|
@@ -245,7 +264,7 @@ class GuiManuscript(QDialog):
|
|
245
264
|
if isinstance(build, BuildSettings):
|
246
265
|
self._updatePreview(data, build)
|
247
266
|
except Exception:
|
248
|
-
logger.error("Failed to
|
267
|
+
logger.error("Failed to load build cache")
|
249
268
|
logException()
|
250
269
|
return
|
251
270
|
|
@@ -284,25 +303,21 @@ class GuiManuscript(QDialog):
|
|
284
303
|
@pyqtSlot()
|
285
304
|
def _editSelectedBuild(self) -> None:
|
286
305
|
"""Edit the currently selected build settings entry."""
|
287
|
-
build
|
288
|
-
if build is not None:
|
306
|
+
if build := self._getSelectedBuild():
|
289
307
|
self._openSettingsDialog(build)
|
290
308
|
return
|
291
309
|
|
292
310
|
@pyqtSlot("QListWidgetItem*", "QListWidgetItem*")
|
293
311
|
def _updateBuildDetails(self, current: QListWidgetItem, previous: QListWidgetItem) -> None:
|
294
312
|
"""Process change of build selection to update the details."""
|
295
|
-
if
|
296
|
-
|
297
|
-
if build is not None:
|
298
|
-
self.buildDetails.updateInfo(build)
|
313
|
+
if current and (build := self._builds.getBuild(current.data(self.D_KEY))):
|
314
|
+
self.buildDetails.updateInfo(build)
|
299
315
|
return
|
300
316
|
|
301
317
|
@pyqtSlot()
|
302
318
|
def _deleteSelectedBuild(self) -> None:
|
303
319
|
"""Delete the currently selected build settings entry."""
|
304
|
-
build
|
305
|
-
if build is not None:
|
320
|
+
if build := self._getSelectedBuild():
|
306
321
|
if SHARED.question(self.tr("Delete build '{0}'?".format(build.name))):
|
307
322
|
self._builds.removeBuild(build.buildID)
|
308
323
|
self._updateBuildsList()
|
@@ -313,8 +328,7 @@ class GuiManuscript(QDialog):
|
|
313
328
|
"""Process new build settings from the settings dialog."""
|
314
329
|
self._builds.setBuild(build)
|
315
330
|
self._updateBuildItem(build)
|
316
|
-
current
|
317
|
-
if isinstance(current, QListWidgetItem) and current.data(self.D_KEY) == build.buildID:
|
331
|
+
if (current := self.buildList.currentItem()) and current.data(self.D_KEY) == build.buildID:
|
318
332
|
self._updateBuildDetails(current, current)
|
319
333
|
return
|
320
334
|
|
@@ -323,23 +337,25 @@ class GuiManuscript(QDialog):
|
|
323
337
|
"""Run the document builder on the current build settings for
|
324
338
|
the preview widget.
|
325
339
|
"""
|
326
|
-
build
|
327
|
-
if build is None:
|
340
|
+
if not (build := self._getSelectedBuild()):
|
328
341
|
return
|
329
342
|
|
330
343
|
docBuild = NWBuildDocument(SHARED.project, build)
|
344
|
+
docBuild.setPreviewMode(True)
|
331
345
|
docBuild.queueAll()
|
332
346
|
|
333
347
|
self.docPreview.beginNewBuild(len(docBuild))
|
334
348
|
for step, _ in docBuild.iterBuildHTML(None):
|
335
349
|
self.docPreview.buildStep(step + 1)
|
336
|
-
|
350
|
+
QApplication.processEvents()
|
337
351
|
|
338
352
|
buildObj = docBuild.lastBuild
|
339
353
|
assert isinstance(buildObj, ToHtml)
|
340
354
|
result = {
|
341
355
|
"uuid": build.buildID,
|
342
356
|
"time": int(time()),
|
357
|
+
"stats": buildObj.textStats,
|
358
|
+
"outline": buildObj.textOutline,
|
343
359
|
"styles": buildObj.getStyleSheet(),
|
344
360
|
"html": buildObj.fullHTML,
|
345
361
|
}
|
@@ -361,10 +377,9 @@ class GuiManuscript(QDialog):
|
|
361
377
|
@pyqtSlot()
|
362
378
|
def _buildManuscript(self) -> None:
|
363
379
|
"""Open the build dialog and build the manuscript."""
|
364
|
-
build
|
365
|
-
if isinstance(build, BuildSettings):
|
380
|
+
if build := self._getSelectedBuild():
|
366
381
|
dlgBuild = GuiManuscriptBuild(self, build)
|
367
|
-
dlgBuild.
|
382
|
+
dlgBuild.exec()
|
368
383
|
|
369
384
|
# After the build is done, save build settings changes
|
370
385
|
if build.changed:
|
@@ -377,7 +392,7 @@ class GuiManuscript(QDialog):
|
|
377
392
|
"""Open the print preview dialog."""
|
378
393
|
preview = QPrintPreviewDialog(self)
|
379
394
|
preview.paintRequested.connect(self.docPreview.printPreview)
|
380
|
-
preview.
|
395
|
+
preview.exec()
|
381
396
|
return
|
382
397
|
|
383
398
|
##
|
@@ -395,6 +410,8 @@ class GuiManuscript(QDialog):
|
|
395
410
|
self.docPreview.setJustify(
|
396
411
|
build.getBool("format.justifyText")
|
397
412
|
)
|
413
|
+
self.docStats.updateStats(data.get("stats", {}))
|
414
|
+
self.buildOutline.updateOutline(data.get("outline", {}))
|
398
415
|
return
|
399
416
|
|
400
417
|
def _getSelectedBuild(self) -> BuildSettings | None:
|
@@ -463,7 +480,7 @@ class GuiManuscript(QDialog):
|
|
463
480
|
dlgSettings.setModal(False)
|
464
481
|
dlgSettings.show()
|
465
482
|
dlgSettings.raise_()
|
466
|
-
|
483
|
+
QApplication.processEvents()
|
467
484
|
dlgSettings.loadContent()
|
468
485
|
dlgSettings.newSettingsReady.connect(self._processNewSettings)
|
469
486
|
|
@@ -503,8 +520,8 @@ class _DetailsWidget(QWidget):
|
|
503
520
|
# Tree Widget
|
504
521
|
self.listView = QTreeWidget(self)
|
505
522
|
self.listView.setHeaderLabels([self.tr("Setting"), self.tr("Value")])
|
506
|
-
self.listView.setIndentation(SHARED.theme.
|
507
|
-
self.listView.setSelectionMode(QAbstractItemView.NoSelection)
|
523
|
+
self.listView.setIndentation(SHARED.theme.baseIconHeight)
|
524
|
+
self.listView.setSelectionMode(QAbstractItemView.SelectionMode.NoSelection)
|
508
525
|
|
509
526
|
# Assemble
|
510
527
|
self.outerBox = QVBoxLayout()
|
@@ -592,24 +609,26 @@ class _DetailsWidget(QWidget):
|
|
592
609
|
hFmt.resetScene()
|
593
610
|
hFmt.incScene()
|
594
611
|
title = self.tr("Title")
|
612
|
+
hidden = self.tr("Hidden")
|
595
613
|
|
596
614
|
item = QTreeWidgetItem()
|
597
615
|
item.setText(0, build.getLabel("headings"))
|
598
616
|
item.setText(1, "")
|
599
617
|
self.listView.addTopLevelItem(item)
|
600
|
-
|
601
|
-
"headings.fmtTitle", "headings.
|
602
|
-
"headings.
|
603
|
-
|
604
|
-
|
618
|
+
for hFormat, hHide in [
|
619
|
+
("headings.fmtTitle", "headings.hideTitle"),
|
620
|
+
("headings.fmtChapter", "headings.hideChapter"),
|
621
|
+
("headings.fmtUnnumbered", "headings.hideUnnumbered"),
|
622
|
+
("headings.fmtScene", "headings.hideScene"),
|
623
|
+
("headings.fmtAltScene", "headings.hideAltScene"),
|
624
|
+
("headings.fmtSection", "headings.hideSection"),
|
625
|
+
]:
|
605
626
|
sub = QTreeWidgetItem()
|
606
|
-
sub.setText(0, build.getLabel(
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
sub.setText(0, build.getLabel(key))
|
612
|
-
sub.setIcon(1, on if build.getBool(key) else off)
|
627
|
+
sub.setText(0, build.getLabel(hFormat))
|
628
|
+
if build.getBool(hHide):
|
629
|
+
sub.setText(1, f"[{hidden}]")
|
630
|
+
else:
|
631
|
+
sub.setText(1, hFmt.apply(build.getStr(hFormat), title, 0))
|
613
632
|
item.addChild(sub)
|
614
633
|
|
615
634
|
# Text Content
|
@@ -617,11 +636,10 @@ class _DetailsWidget(QWidget):
|
|
617
636
|
item.setText(0, build.getLabel("text.grpContent"))
|
618
637
|
item.setText(1, "")
|
619
638
|
self.listView.addTopLevelItem(item)
|
620
|
-
|
639
|
+
for key in [
|
621
640
|
"text.includeSynopsis", "text.includeComments",
|
622
641
|
"text.includeKeywords", "text.includeBodyText",
|
623
|
-
]
|
624
|
-
for key in entries:
|
642
|
+
]:
|
625
643
|
sub = QTreeWidgetItem()
|
626
644
|
sub.setText(0, build.getLabel(key))
|
627
645
|
sub.setIcon(1, on if build.getBool(key) else off)
|
@@ -635,6 +653,87 @@ class _DetailsWidget(QWidget):
|
|
635
653
|
# END Class _DetailsWidget
|
636
654
|
|
637
655
|
|
656
|
+
class _OutlineWidget(QWidget):
|
657
|
+
|
658
|
+
D_LINE = QtUserRole
|
659
|
+
|
660
|
+
outlineEntryClicked = pyqtSignal(str)
|
661
|
+
|
662
|
+
def __init__(self, parent: QWidget) -> None:
|
663
|
+
super().__init__(parent=parent)
|
664
|
+
|
665
|
+
self._outline = {}
|
666
|
+
|
667
|
+
# Tree Widget
|
668
|
+
self.listView = QTreeWidget(self)
|
669
|
+
self.listView.setHeaderHidden(True)
|
670
|
+
self.listView.setSelectionMode(QAbstractItemView.SelectionMode.NoSelection)
|
671
|
+
self.listView.itemClicked.connect(self._onItemClick)
|
672
|
+
|
673
|
+
# Assemble
|
674
|
+
self.outerBox = QVBoxLayout()
|
675
|
+
self.outerBox.addWidget(self.listView)
|
676
|
+
self.outerBox.setContentsMargins(0, 0, 0, 0)
|
677
|
+
self.setLayout(self.outerBox)
|
678
|
+
|
679
|
+
return
|
680
|
+
|
681
|
+
def updateOutline(self, data: dict[str, str]) -> None:
|
682
|
+
"""Update the outline."""
|
683
|
+
if isinstance(data, dict) and data != self._outline:
|
684
|
+
self.listView.clear()
|
685
|
+
|
686
|
+
tFont = self.font()
|
687
|
+
tFont.setBold(True)
|
688
|
+
tBrush = self.palette().highlight()
|
689
|
+
|
690
|
+
hFont = self.font()
|
691
|
+
hFont.setBold(True)
|
692
|
+
hFont.setUnderline(True)
|
693
|
+
|
694
|
+
root = self.listView.invisibleRootItem()
|
695
|
+
parent = root
|
696
|
+
indent = False
|
697
|
+
for anchor, entry in data.items():
|
698
|
+
prefix, _, text = entry.partition("|")
|
699
|
+
if prefix in ("TT", "PT", "CH", "SC", "H1", "H2"):
|
700
|
+
item = QTreeWidgetItem([text])
|
701
|
+
item.setData(0, self.D_LINE, anchor)
|
702
|
+
if prefix == "TT":
|
703
|
+
item.setFont(0, tFont)
|
704
|
+
item.setForeground(0, tBrush)
|
705
|
+
root.addChild(item)
|
706
|
+
parent = root
|
707
|
+
elif prefix == "PT":
|
708
|
+
item.setFont(0, hFont)
|
709
|
+
root.addChild(item)
|
710
|
+
parent = root
|
711
|
+
elif prefix in ("CH", "H1"):
|
712
|
+
root.addChild(item)
|
713
|
+
parent = item
|
714
|
+
elif prefix in ("SC", "H2"):
|
715
|
+
parent.addChild(item)
|
716
|
+
indent = True
|
717
|
+
|
718
|
+
self.listView.setIndentation(
|
719
|
+
SHARED.theme.baseIconHeight if indent else CONFIG.pxInt(4)
|
720
|
+
)
|
721
|
+
self._outline = data
|
722
|
+
|
723
|
+
return
|
724
|
+
|
725
|
+
##
|
726
|
+
# Private Slots
|
727
|
+
##
|
728
|
+
|
729
|
+
def _onItemClick(self, item: QTreeWidgetItem) -> None:
|
730
|
+
"""Process tree item click."""
|
731
|
+
self.outlineEntryClicked.emit(str(item.data(0, self.D_LINE)))
|
732
|
+
return
|
733
|
+
|
734
|
+
# END Class _OutlineWidget
|
735
|
+
|
736
|
+
|
638
737
|
class _PreviewWidget(QTextBrowser):
|
639
738
|
|
640
739
|
def __init__(self, parent: QWidget) -> None:
|
@@ -645,8 +744,8 @@ class _PreviewWidget(QTextBrowser):
|
|
645
744
|
|
646
745
|
# Document Setup
|
647
746
|
dPalette = self.palette()
|
648
|
-
dPalette.setColor(QPalette.Base, QColor(255, 255, 255))
|
649
|
-
dPalette.setColor(QPalette.Text, QColor(0, 0, 0))
|
747
|
+
dPalette.setColor(QPalette.ColorRole.Base, QColor(255, 255, 255))
|
748
|
+
dPalette.setColor(QPalette.ColorRole.Text, QColor(0, 0, 0))
|
650
749
|
self.setPalette(dPalette)
|
651
750
|
|
652
751
|
self.setMinimumWidth(40*SHARED.theme.textNWidth)
|
@@ -663,8 +762,8 @@ class _PreviewWidget(QTextBrowser):
|
|
663
762
|
|
664
763
|
# Document Age
|
665
764
|
aPalette = self.palette()
|
666
|
-
aPalette.setColor(QPalette.
|
667
|
-
aPalette.setColor(QPalette.
|
765
|
+
aPalette.setColor(QPalette.ColorRole.Window, aPalette.toolTipBase().color())
|
766
|
+
aPalette.setColor(QPalette.ColorRole.WindowText, aPalette.toolTipText().color())
|
668
767
|
|
669
768
|
aFont = self.font()
|
670
769
|
aFont.setPointSizeF(0.9*SHARED.theme.fontPointSize)
|
@@ -674,7 +773,7 @@ class _PreviewWidget(QTextBrowser):
|
|
674
773
|
self.ageLabel.setFont(aFont)
|
675
774
|
self.ageLabel.setPalette(aPalette)
|
676
775
|
self.ageLabel.setAutoFillBackground(True)
|
677
|
-
self.ageLabel.setAlignment(
|
776
|
+
self.ageLabel.setAlignment(QtAlignCenter)
|
678
777
|
self.ageLabel.setFixedHeight(int(2.1*SHARED.theme.fontPixelSize))
|
679
778
|
|
680
779
|
# Progress
|
@@ -692,8 +791,8 @@ class _PreviewWidget(QTextBrowser):
|
|
692
791
|
self._updateBuildAge()
|
693
792
|
|
694
793
|
# Age Timer
|
695
|
-
self.ageTimer = QTimer()
|
696
|
-
self.ageTimer.setInterval(
|
794
|
+
self.ageTimer = QTimer(self)
|
795
|
+
self.ageTimer.setInterval(10000)
|
697
796
|
self.ageTimer.timeout.connect(self._updateBuildAge)
|
698
797
|
self.ageTimer.start()
|
699
798
|
|
@@ -713,9 +812,9 @@ class _PreviewWidget(QTextBrowser):
|
|
713
812
|
"""Enable/disable the justify text option."""
|
714
813
|
pOptions = self.document().defaultTextOption()
|
715
814
|
if state:
|
716
|
-
pOptions.setAlignment(
|
815
|
+
pOptions.setAlignment(QtAlignJustify)
|
717
816
|
else:
|
718
|
-
pOptions.setAlignment(
|
817
|
+
pOptions.setAlignment(QtAlignAbsolute)
|
719
818
|
self.document().setDefaultTextOption(pOptions)
|
720
819
|
return
|
721
820
|
|
@@ -745,31 +844,24 @@ class _PreviewWidget(QTextBrowser):
|
|
745
844
|
def buildStep(self, value: int) -> None:
|
746
845
|
"""Update the progress bar value."""
|
747
846
|
self.buildProgress.setValue(value)
|
748
|
-
|
847
|
+
QApplication.processEvents()
|
749
848
|
return
|
750
849
|
|
751
850
|
def setContent(self, data: dict) -> None:
|
752
851
|
"""Set the content of the preview widget."""
|
753
852
|
sPos = self.verticalScrollBar().value()
|
754
|
-
|
853
|
+
QApplication.setOverrideCursor(QCursor(Qt.CursorShape.WaitCursor))
|
755
854
|
|
756
855
|
self.buildProgress.setCentreText(self.tr("Processing ..."))
|
757
|
-
|
758
|
-
|
759
|
-
styles = "\n".join(data.get("styles", [
|
760
|
-
"h1, h2 {color: rgb(66, 113, 174);}",
|
761
|
-
"h3, h4 {color: rgb(50, 50, 50);}",
|
762
|
-
"a {color: rgb(66, 113, 174);}",
|
763
|
-
".tags {color: rgb(245, 135, 31); font-weight: bold;}",
|
764
|
-
]))
|
856
|
+
QApplication.processEvents()
|
857
|
+
|
858
|
+
styles = "\n".join(data.get("styles", []))
|
765
859
|
self.document().setDefaultStyleSheet(styles)
|
766
860
|
|
767
861
|
html = "".join(data.get("html", []))
|
768
862
|
html = html.replace("\t", "!!tab!!")
|
769
|
-
html = html.replace("<del>", "<span style='text-decoration: line-through;'>")
|
770
|
-
html = html.replace("</del>", "</span>")
|
771
863
|
self.setHtml(html)
|
772
|
-
|
864
|
+
QApplication.processEvents()
|
773
865
|
while self.find("!!tab!!"):
|
774
866
|
cursor = self.textCursor()
|
775
867
|
cursor.insertText("\t")
|
@@ -783,8 +875,8 @@ class _PreviewWidget(QTextBrowser):
|
|
783
875
|
self.document().markContentsDirty(0, self.document().characterCount())
|
784
876
|
|
785
877
|
self.buildProgress.setCentreText(self.tr("Done"))
|
786
|
-
|
787
|
-
|
878
|
+
QApplication.restoreOverrideCursor()
|
879
|
+
QApplication.processEvents()
|
788
880
|
QTimer.singleShot(300, self._hideProgress)
|
789
881
|
|
790
882
|
return
|
@@ -806,10 +898,17 @@ class _PreviewWidget(QTextBrowser):
|
|
806
898
|
@pyqtSlot("QPrinter*")
|
807
899
|
def printPreview(self, printer: QPrinter) -> None:
|
808
900
|
"""Connect the print preview painter to the document viewer."""
|
809
|
-
|
810
|
-
printer.setOrientation(QPrinter.Portrait)
|
901
|
+
QApplication.setOverrideCursor(QCursor(Qt.CursorShape.WaitCursor))
|
902
|
+
printer.setOrientation(QPrinter.Orientation.Portrait)
|
811
903
|
self.document().print(printer)
|
812
|
-
|
904
|
+
QApplication.restoreOverrideCursor()
|
905
|
+
return
|
906
|
+
|
907
|
+
@pyqtSlot(str)
|
908
|
+
def navigateTo(self, anchor: str) -> None:
|
909
|
+
"""Go to a specific #link in the document."""
|
910
|
+
logger.debug("Moving to anchor '#%s'", anchor)
|
911
|
+
self.setSource(QUrl(f"#{anchor}"))
|
813
912
|
return
|
814
913
|
|
815
914
|
##
|
@@ -821,7 +920,7 @@ class _PreviewWidget(QTextBrowser):
|
|
821
920
|
"""Update the build time and the fuzzy age."""
|
822
921
|
if self._docTime > 0:
|
823
922
|
strBuildTime = "%s (%s)" % (
|
824
|
-
datetime.fromtimestamp(self._docTime)
|
923
|
+
CONFIG.localDateTime(datetime.fromtimestamp(self._docTime)),
|
825
924
|
fuzzyTime(int(time()) - self._docTime)
|
826
925
|
)
|
827
926
|
else:
|
@@ -859,3 +958,181 @@ class _PreviewWidget(QTextBrowser):
|
|
859
958
|
return
|
860
959
|
|
861
960
|
# END Class _PreviewWidget
|
961
|
+
|
962
|
+
|
963
|
+
class _StatsWidget(QWidget):
|
964
|
+
|
965
|
+
def __init__(self, parent: QWidget) -> None:
|
966
|
+
super().__init__(parent=parent)
|
967
|
+
|
968
|
+
font = self.font()
|
969
|
+
font.setPointSizeF(0.9*SHARED.theme.fontPointSize)
|
970
|
+
self.setFont(font)
|
971
|
+
|
972
|
+
self.minWidget = QWidget(self)
|
973
|
+
self.maxWidget = QWidget(self)
|
974
|
+
|
975
|
+
self.toggleButton = NIconToggleButton(self, SHARED.theme.baseIconSize, "unfold")
|
976
|
+
self.toggleButton.toggled.connect(self._toggleView)
|
977
|
+
|
978
|
+
self._buildMinimal()
|
979
|
+
self._buildMaximal()
|
980
|
+
|
981
|
+
self.mainStack = QStackedWidget(self)
|
982
|
+
self.mainStack.addWidget(self.minWidget)
|
983
|
+
self.mainStack.addWidget(self.maxWidget)
|
984
|
+
|
985
|
+
self.outerBox = QHBoxLayout()
|
986
|
+
self.outerBox.addWidget(self.toggleButton, 0, QtAlignTop)
|
987
|
+
self.outerBox.addWidget(self.mainStack, 1, QtAlignTop)
|
988
|
+
self.outerBox.setContentsMargins(0, 0, 0, 0)
|
989
|
+
|
990
|
+
self.setLayout(self.outerBox)
|
991
|
+
|
992
|
+
self._toggleView(False)
|
993
|
+
|
994
|
+
return
|
995
|
+
|
996
|
+
def updateStats(self, data: dict[str, int]) -> None:
|
997
|
+
"""Update the stats values from a Tokenizer stats dict."""
|
998
|
+
# Minimal
|
999
|
+
self.minWordCount.setText("{0:n}".format(data.get("allWords", 0)))
|
1000
|
+
self.minCharCount.setText("{0:n}".format(data.get("allChars", 0)))
|
1001
|
+
|
1002
|
+
# Maximal
|
1003
|
+
self.maxTotalWords.setText("{0:n}".format(data.get("allWords", 0)))
|
1004
|
+
self.maxHeadWords.setText("{0:n}".format(data.get("titleWords", 0)))
|
1005
|
+
self.maxTextWords.setText("{0:n}".format(data.get("textWords", 0)))
|
1006
|
+
self.maxTitleCount.setText("{0:n}".format(data.get("titleCount", 0)))
|
1007
|
+
self.maxParCount.setText("{0:n}".format(data.get("paragraphCount", 0)))
|
1008
|
+
|
1009
|
+
self.maxTotalChars.setText("{0:n}".format(data.get("allChars", 0)))
|
1010
|
+
self.maxHeaderChars.setText("{0:n}".format(data.get("titleChars", 0)))
|
1011
|
+
self.maxTextChars.setText("{0:n}".format(data.get("textChars", 0)))
|
1012
|
+
|
1013
|
+
self.maxTotalWordChars.setText("{0:n}".format(data.get("allWordChars", 0)))
|
1014
|
+
self.maxHeadWordChars.setText("{0:n}".format(data.get("titleWordChars", 0)))
|
1015
|
+
self.maxTextWordChars.setText("{0:n}".format(data.get("textWordChars", 0)))
|
1016
|
+
|
1017
|
+
return
|
1018
|
+
|
1019
|
+
##
|
1020
|
+
# Private Slots
|
1021
|
+
##
|
1022
|
+
|
1023
|
+
@pyqtSlot(bool)
|
1024
|
+
def _toggleView(self, state: bool) -> None:
|
1025
|
+
"""Toggle minimal or maximal view."""
|
1026
|
+
ignored = QSizePolicy.Policy.Ignored
|
1027
|
+
expanded = QSizePolicy.Policy.Expanding
|
1028
|
+
if state:
|
1029
|
+
self.mainStack.setCurrentWidget(self.maxWidget)
|
1030
|
+
self.maxWidget.setSizePolicy(expanded, expanded)
|
1031
|
+
self.minWidget.setSizePolicy(ignored, ignored)
|
1032
|
+
else:
|
1033
|
+
self.mainStack.setCurrentWidget(self.minWidget)
|
1034
|
+
self.maxWidget.setSizePolicy(ignored, ignored)
|
1035
|
+
self.minWidget.setSizePolicy(expanded, expanded)
|
1036
|
+
self.maxWidget.adjustSize()
|
1037
|
+
self.minWidget.adjustSize()
|
1038
|
+
self.mainStack.adjustSize()
|
1039
|
+
self.adjustSize()
|
1040
|
+
return
|
1041
|
+
|
1042
|
+
##
|
1043
|
+
# Internal Functions
|
1044
|
+
##
|
1045
|
+
|
1046
|
+
def _buildMinimal(self) -> None:
|
1047
|
+
"""Build the minimal stats page."""
|
1048
|
+
mPx = CONFIG.pxInt(8)
|
1049
|
+
|
1050
|
+
self.lblWordCount = QLabel(self.tr("Words"), self)
|
1051
|
+
self.minWordCount = QLabel(self)
|
1052
|
+
|
1053
|
+
self.lblCharCount = QLabel(self.tr("Characters"), self)
|
1054
|
+
self.minCharCount = QLabel(self)
|
1055
|
+
|
1056
|
+
# Assemble
|
1057
|
+
self.minLayout = QHBoxLayout()
|
1058
|
+
self.minLayout.addWidget(self.lblWordCount)
|
1059
|
+
self.minLayout.addWidget(self.minWordCount)
|
1060
|
+
self.minLayout.addSpacing(mPx)
|
1061
|
+
self.minLayout.addWidget(self.lblCharCount)
|
1062
|
+
self.minLayout.addWidget(self.minCharCount)
|
1063
|
+
self.minLayout.addStretch(1)
|
1064
|
+
self.minLayout.setSpacing(mPx)
|
1065
|
+
self.minLayout.setContentsMargins(0, 0, 0, 0)
|
1066
|
+
|
1067
|
+
self.minWidget.setLayout(self.minLayout)
|
1068
|
+
|
1069
|
+
return
|
1070
|
+
|
1071
|
+
def _buildMaximal(self) -> None:
|
1072
|
+
"""Build the maximal stats page."""
|
1073
|
+
hPx = CONFIG.pxInt(12)
|
1074
|
+
vPx = CONFIG.pxInt(4)
|
1075
|
+
|
1076
|
+
# Left Column
|
1077
|
+
self.maxTotalWords = QLabel(self)
|
1078
|
+
self.maxHeadWords = QLabel(self)
|
1079
|
+
self.maxTextWords = QLabel(self)
|
1080
|
+
self.maxTitleCount = QLabel(self)
|
1081
|
+
self.maxParCount = QLabel(self)
|
1082
|
+
|
1083
|
+
self.maxTotalWords.setAlignment(QtAlignRight)
|
1084
|
+
self.maxHeadWords.setAlignment(QtAlignRight)
|
1085
|
+
self.maxTextWords.setAlignment(QtAlignRight)
|
1086
|
+
self.maxTitleCount.setAlignment(QtAlignRight)
|
1087
|
+
self.maxParCount.setAlignment(QtAlignRight)
|
1088
|
+
|
1089
|
+
self.leftForm = QFormLayout()
|
1090
|
+
self.leftForm.addRow(self.tr("Words"), self.maxTotalWords)
|
1091
|
+
self.leftForm.addRow(self.tr("Words in Headings"), self.maxHeadWords)
|
1092
|
+
self.leftForm.addRow(self.tr("Words in Text"), self.maxTextWords)
|
1093
|
+
self.leftForm.addRow("", QLabel(self))
|
1094
|
+
self.leftForm.addRow(self.tr("Headings"), self.maxTitleCount)
|
1095
|
+
self.leftForm.addRow(self.tr("Paragraphs"), self.maxParCount)
|
1096
|
+
self.leftForm.setHorizontalSpacing(hPx)
|
1097
|
+
self.leftForm.setVerticalSpacing(vPx)
|
1098
|
+
|
1099
|
+
# Right Column
|
1100
|
+
self.maxTotalChars = QLabel(self)
|
1101
|
+
self.maxHeaderChars = QLabel(self)
|
1102
|
+
self.maxTextChars = QLabel(self)
|
1103
|
+
|
1104
|
+
self.maxTotalWordChars = QLabel(self)
|
1105
|
+
self.maxHeadWordChars = QLabel(self)
|
1106
|
+
self.maxTextWordChars = QLabel(self)
|
1107
|
+
|
1108
|
+
self.maxTotalChars.setAlignment(QtAlignRight)
|
1109
|
+
self.maxHeaderChars.setAlignment(QtAlignRight)
|
1110
|
+
self.maxTextChars.setAlignment(QtAlignRight)
|
1111
|
+
|
1112
|
+
self.maxTotalWordChars.setAlignment(QtAlignRight)
|
1113
|
+
self.maxHeadWordChars.setAlignment(QtAlignRight)
|
1114
|
+
self.maxTextWordChars.setAlignment(QtAlignRight)
|
1115
|
+
|
1116
|
+
self.rightForm = QFormLayout()
|
1117
|
+
self.rightForm.addRow(self.tr("Characters"), self.maxTotalChars)
|
1118
|
+
self.rightForm.addRow(self.tr("Characters in Headings"), self.maxHeaderChars)
|
1119
|
+
self.rightForm.addRow(self.tr("Characters in Text"), self.maxTextChars)
|
1120
|
+
self.rightForm.addRow(self.tr("Characters, No Spaces"), self.maxTotalWordChars)
|
1121
|
+
self.rightForm.addRow(self.tr("Characters in Headings, No Spaces"), self.maxHeadWordChars)
|
1122
|
+
self.rightForm.addRow(self.tr("Characters in Text, No Spaces"), self.maxTextWordChars)
|
1123
|
+
self.rightForm.setHorizontalSpacing(hPx)
|
1124
|
+
self.rightForm.setVerticalSpacing(vPx)
|
1125
|
+
|
1126
|
+
# Assemble
|
1127
|
+
self.maxLayout = QHBoxLayout()
|
1128
|
+
self.maxLayout.addLayout(self.leftForm)
|
1129
|
+
self.maxLayout.addLayout(self.rightForm)
|
1130
|
+
self.maxLayout.addStretch(1)
|
1131
|
+
self.maxLayout.setSpacing(CONFIG.pxInt(32))
|
1132
|
+
self.maxLayout.setContentsMargins(0, 0, 0, 0)
|
1133
|
+
|
1134
|
+
self.maxWidget.setLayout(self.maxLayout)
|
1135
|
+
|
1136
|
+
return
|
1137
|
+
|
1138
|
+
# END Class _StatsWidget
|