novelWriter 2.2.1__py3-none-any.whl → 2.3b1__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.
Files changed (110) hide show
  1. {novelWriter-2.2.1.dist-info → novelWriter-2.3b1.dist-info}/METADATA +1 -1
  2. {novelWriter-2.2.1.dist-info → novelWriter-2.3b1.dist-info}/RECORD +102 -92
  3. novelwriter/__init__.py +4 -4
  4. novelwriter/assets/icons/typicons_dark/icons.conf +6 -0
  5. novelwriter/assets/icons/typicons_dark/mixed_import.svg +5 -0
  6. novelwriter/assets/icons/typicons_dark/typ_document-add-col.svg +8 -0
  7. novelwriter/assets/icons/typicons_dark/typ_document-add.svg +4 -0
  8. novelwriter/assets/icons/typicons_dark/typ_document.svg +4 -0
  9. novelwriter/assets/icons/typicons_dark/typ_th-dot-more.svg +4 -0
  10. novelwriter/assets/icons/typicons_light/icons.conf +6 -0
  11. novelwriter/assets/icons/typicons_light/mixed_import.svg +5 -0
  12. novelwriter/assets/icons/typicons_light/typ_document-add-col.svg +8 -0
  13. novelwriter/assets/icons/typicons_light/typ_document-add.svg +4 -0
  14. novelwriter/assets/icons/typicons_light/typ_document.svg +4 -0
  15. novelwriter/assets/icons/typicons_light/typ_th-dot-more.svg +4 -0
  16. novelwriter/assets/images/novelwriter-text-dark.svg +4 -0
  17. novelwriter/assets/images/novelwriter-text-light.svg +4 -0
  18. novelwriter/assets/images/welcome-dark.jpg +0 -0
  19. novelwriter/assets/images/welcome-light.jpg +0 -0
  20. novelwriter/assets/manual.pdf +0 -0
  21. novelwriter/assets/sample.zip +0 -0
  22. novelwriter/assets/syntax/default_dark.conf +1 -0
  23. novelwriter/assets/syntax/default_light.conf +1 -0
  24. novelwriter/assets/syntax/grey_dark.conf +1 -0
  25. novelwriter/assets/syntax/grey_light.conf +1 -0
  26. novelwriter/assets/syntax/light_owl.conf +1 -0
  27. novelwriter/assets/syntax/night_owl.conf +1 -0
  28. novelwriter/assets/syntax/solarized_dark.conf +1 -0
  29. novelwriter/assets/syntax/solarized_light.conf +1 -0
  30. novelwriter/assets/syntax/tomorrow.conf +1 -0
  31. novelwriter/assets/syntax/tomorrow_night.conf +1 -0
  32. novelwriter/assets/syntax/tomorrow_night_blue.conf +1 -0
  33. novelwriter/assets/syntax/tomorrow_night_bright.conf +1 -0
  34. novelwriter/assets/syntax/tomorrow_night_eighties.conf +1 -0
  35. novelwriter/assets/text/credits_en.htm +4 -2
  36. novelwriter/assets/themes/default_dark.conf +2 -2
  37. novelwriter/assets/themes/default_light.conf +2 -2
  38. novelwriter/common.py +48 -37
  39. novelwriter/config.py +36 -41
  40. novelwriter/constants.py +38 -16
  41. novelwriter/core/buildsettings.py +7 -7
  42. novelwriter/core/coretools.py +192 -154
  43. novelwriter/core/docbuild.py +6 -3
  44. novelwriter/core/document.py +6 -6
  45. novelwriter/core/index.py +89 -56
  46. novelwriter/core/item.py +21 -3
  47. novelwriter/core/options.py +8 -7
  48. novelwriter/core/project.py +69 -44
  49. novelwriter/core/projectdata.py +1 -14
  50. novelwriter/core/projectxml.py +13 -41
  51. novelwriter/core/sessions.py +2 -1
  52. novelwriter/core/spellcheck.py +2 -1
  53. novelwriter/core/status.py +2 -1
  54. novelwriter/core/storage.py +178 -140
  55. novelwriter/core/tohtml.py +4 -2
  56. novelwriter/core/tokenizer.py +73 -45
  57. novelwriter/core/toodt.py +40 -30
  58. novelwriter/core/tree.py +3 -2
  59. novelwriter/dialogs/about.py +70 -160
  60. novelwriter/dialogs/docmerge.py +6 -5
  61. novelwriter/dialogs/docsplit.py +6 -6
  62. novelwriter/dialogs/editlabel.py +1 -1
  63. novelwriter/dialogs/preferences.py +553 -703
  64. novelwriter/dialogs/{projsettings.py → projectsettings.py} +288 -262
  65. novelwriter/dialogs/quotes.py +27 -23
  66. novelwriter/dialogs/wordlist.py +96 -40
  67. novelwriter/enum.py +20 -18
  68. novelwriter/error.py +1 -1
  69. novelwriter/extensions/circularprogress.py +11 -11
  70. novelwriter/extensions/configlayout.py +185 -134
  71. novelwriter/extensions/modified.py +81 -0
  72. novelwriter/extensions/novelselector.py +26 -12
  73. novelwriter/extensions/pagedsidebar.py +14 -16
  74. novelwriter/extensions/simpleprogress.py +5 -5
  75. novelwriter/extensions/statusled.py +8 -8
  76. novelwriter/extensions/switch.py +31 -63
  77. novelwriter/extensions/switchbox.py +1 -1
  78. novelwriter/extensions/versioninfo.py +153 -0
  79. novelwriter/gui/doceditor.py +178 -150
  80. novelwriter/gui/dochighlight.py +63 -92
  81. novelwriter/gui/docviewer.py +49 -51
  82. novelwriter/gui/docviewerpanel.py +72 -24
  83. novelwriter/gui/itemdetails.py +7 -7
  84. novelwriter/gui/mainmenu.py +14 -18
  85. novelwriter/gui/noveltree.py +9 -8
  86. novelwriter/gui/outline.py +98 -75
  87. novelwriter/gui/projtree.py +188 -61
  88. novelwriter/gui/sidebar.py +3 -4
  89. novelwriter/gui/statusbar.py +3 -4
  90. novelwriter/gui/theme.py +60 -68
  91. novelwriter/guimain.py +49 -156
  92. novelwriter/shared.py +15 -1
  93. novelwriter/tools/dictionaries.py +5 -6
  94. novelwriter/tools/manuscript.py +6 -6
  95. novelwriter/tools/manussettings.py +192 -221
  96. novelwriter/tools/noveldetails.py +525 -0
  97. novelwriter/tools/welcome.py +802 -0
  98. novelwriter/tools/writingstats.py +9 -9
  99. novelwriter/assets/images/wizard-back.jpg +0 -0
  100. novelwriter/assets/text/gplv3_en.htm +0 -641
  101. novelwriter/assets/text/release_notes.htm +0 -60
  102. novelwriter/dialogs/projdetails.py +0 -518
  103. novelwriter/dialogs/projload.py +0 -294
  104. novelwriter/dialogs/updates.py +0 -172
  105. novelwriter/extensions/pageddialog.py +0 -130
  106. novelwriter/tools/projwizard.py +0 -478
  107. {novelWriter-2.2.1.dist-info → novelWriter-2.3b1.dist-info}/LICENSE.md +0 -0
  108. {novelWriter-2.2.1.dist-info → novelWriter-2.3b1.dist-info}/WHEEL +0 -0
  109. {novelWriter-2.2.1.dist-info → novelWriter-2.3b1.dist-info}/entry_points.txt +0 -0
  110. {novelWriter-2.2.1.dist-info → novelWriter-2.3b1.dist-info}/top_level.txt +0 -0
@@ -25,7 +25,7 @@ from __future__ import annotations
25
25
 
26
26
  import logging
27
27
 
28
- from PyQt5.QtGui import QCloseEvent, QFontMetrics
28
+ from PyQt5.QtGui import QFontMetrics
29
29
  from PyQt5.QtCore import QSize, Qt, pyqtSlot
30
30
  from PyQt5.QtWidgets import (
31
31
  QDialog, QDialogButtonBox, QFrame, QHBoxLayout, QLabel, QListWidget,
@@ -40,11 +40,11 @@ logger = logging.getLogger(__name__)
40
40
 
41
41
  class GuiQuoteSelect(QDialog):
42
42
 
43
- selectedQuote = ""
43
+ _selected = ""
44
44
 
45
45
  D_KEY = Qt.ItemDataRole.UserRole
46
46
 
47
- def __init__(self, parent: QWidget, currentQuote: str = '"') -> None:
47
+ def __init__(self, parent: QWidget, current: str = '"') -> None:
48
48
  super().__init__(parent=parent)
49
49
 
50
50
  logger.debug("Create: GuiQuoteSelect")
@@ -54,7 +54,7 @@ class GuiQuoteSelect(QDialog):
54
54
  self.innerBox = QHBoxLayout()
55
55
  self.labelBox = QVBoxLayout()
56
56
 
57
- self.selectedQuote = currentQuote
57
+ self._selected = current
58
58
 
59
59
  qMetrics = QFontMetrics(self.font())
60
60
  pxW = 7*qMetrics.boundingRectChar("M").width()
@@ -65,7 +65,7 @@ class GuiQuoteSelect(QDialog):
65
65
  lblFont.setPointSizeF(4*lblFont.pointSizeF())
66
66
 
67
67
  # Preview Label
68
- self.previewLabel = QLabel(currentQuote)
68
+ self.previewLabel = QLabel(current)
69
69
  self.previewLabel.setFont(lblFont)
70
70
  self.previewLabel.setFixedSize(QSize(pxW, pxH))
71
71
  self.previewLabel.setAlignment(Qt.AlignCenter)
@@ -77,12 +77,12 @@ class GuiQuoteSelect(QDialog):
77
77
 
78
78
  minSize = 100
79
79
  for sKey, sLabel in nwQuotes.SYMBOLS.items():
80
- theText = "[ %s ] %s" % (sKey, trConst(sLabel))
81
- minSize = max(minSize, qMetrics.boundingRect(theText).width())
82
- qtItem = QListWidgetItem(theText)
80
+ text = "[ %s ] %s" % (sKey, trConst(sLabel))
81
+ minSize = max(minSize, qMetrics.boundingRect(text).width())
82
+ qtItem = QListWidgetItem(text)
83
83
  qtItem.setData(self.D_KEY, sKey)
84
84
  self.listBox.addItem(qtItem)
85
- if sKey == currentQuote:
85
+ if sKey == current:
86
86
  self.listBox.setCurrentItem(qtItem)
87
87
 
88
88
  self.listBox.setMinimumWidth(minSize + CONFIG.pxInt(40))
@@ -113,15 +113,20 @@ class GuiQuoteSelect(QDialog):
113
113
  logger.debug("Delete: GuiQuoteSelect")
114
114
  return
115
115
 
116
- ##
117
- # Events
118
- ##
119
-
120
- def closeEvent(self, event: QCloseEvent) -> None:
121
- """Capture the close event and perform cleanup."""
122
- event.accept()
123
- self.deleteLater()
124
- return
116
+ @property
117
+ def selectedQuote(self) -> str:
118
+ """Return the selected quote symbol."""
119
+ return self._selected
120
+
121
+ @classmethod
122
+ def getQuote(cls, parent: QWidget, current: str = "") -> tuple[str, bool]:
123
+ """Pop the dialog and return the result."""
124
+ cls = GuiQuoteSelect(parent, current=current)
125
+ cls.exec_()
126
+ quote = cls._selected
127
+ accepted = cls.result() == QDialog.DialogCode.Accepted
128
+ cls.deleteLater()
129
+ return quote, accepted
125
130
 
126
131
  ##
127
132
  # Private Slots
@@ -130,11 +135,10 @@ class GuiQuoteSelect(QDialog):
130
135
  @pyqtSlot()
131
136
  def _selectedSymbol(self) -> None:
132
137
  """Update the preview label and the selected quote style."""
133
- selItems = self.listBox.selectedItems()
134
- if selItems:
135
- theSymbol = selItems[0].data(self.D_KEY)
136
- self.previewLabel.setText(theSymbol)
137
- self.selectedQuote = theSymbol
138
+ if items := self.listBox.selectedItems():
139
+ quote = items[0].data(self.D_KEY)
140
+ self.previewLabel.setText(quote)
141
+ self._selected = quote
138
142
  return
139
143
 
140
144
  # END Class GuiQuoteSelect
@@ -26,16 +26,19 @@ from __future__ import annotations
26
26
  import logging
27
27
 
28
28
  from typing import TYPE_CHECKING
29
+ from pathlib import Path
29
30
 
30
31
  from PyQt5.QtGui import QCloseEvent
31
32
  from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot
32
33
  from PyQt5.QtWidgets import (
33
- QAbstractItemView, QDialog, QDialogButtonBox, QHBoxLayout, QLabel,
34
- QLineEdit, QListWidget, QListWidgetItem, QPushButton, QVBoxLayout, qApp
34
+ QAbstractItemView, QDialog, QDialogButtonBox, QFileDialog, QHBoxLayout,
35
+ QLineEdit, QListWidget, QPushButton, QVBoxLayout, qApp
35
36
  )
36
37
 
37
38
  from novelwriter import CONFIG, SHARED
39
+ from novelwriter.common import formatFileFilter
38
40
  from novelwriter.core.spellcheck import UserDictionary
41
+ from novelwriter.extensions.configlayout import NColourLabel
39
42
 
40
43
  if TYPE_CHECKING: # pragma: no cover
41
44
  from novelwriter.guimain import GuiMain
@@ -57,30 +60,46 @@ class GuiWordList(QDialog):
57
60
  mS = CONFIG.pxInt(250)
58
61
  wW = CONFIG.pxInt(320)
59
62
  wH = CONFIG.pxInt(340)
60
- pOptions = SHARED.project.options
61
63
 
62
64
  self.setMinimumWidth(mS)
63
65
  self.setMinimumHeight(mS)
64
66
  self.resize(
65
- CONFIG.pxInt(pOptions.getInt("GuiWordList", "winWidth", wW)),
66
- CONFIG.pxInt(pOptions.getInt("GuiWordList", "winHeight", wH))
67
+ CONFIG.pxInt(SHARED.project.options.getInt("GuiWordList", "winWidth", wW)),
68
+ CONFIG.pxInt(SHARED.project.options.getInt("GuiWordList", "winHeight", wH))
69
+ )
70
+
71
+ # Header
72
+ self.headLabel = NColourLabel(
73
+ "Project Word List", SHARED.theme.helpText, parent=self,
74
+ scale=NColourLabel.HEADER_SCALE
67
75
  )
68
76
 
69
- # Main Widgets
70
- # ============
77
+ self.importButton = QPushButton(SHARED.theme.getIcon("import"), "", self)
78
+ self.importButton.setToolTip(self.tr("Import words from text file"))
79
+ self.importButton.clicked.connect(self._importWords)
71
80
 
72
- self.headLabel = QLabel("<b>%s</b>" % self.tr("Project Word List"))
81
+ self.exportButton = QPushButton(SHARED.theme.getIcon("export"), "", self)
82
+ self.exportButton.setToolTip(self.tr("Export words to text file"))
83
+ self.exportButton.clicked.connect(self._exportWords)
73
84
 
74
- self.listBox = QListWidget()
85
+ self.headerBox = QHBoxLayout()
86
+ self.headerBox.addWidget(self.headLabel, 1)
87
+ self.headerBox.addWidget(self.importButton, 0)
88
+ self.headerBox.addWidget(self.exportButton, 0)
89
+
90
+ # List Box
91
+ self.listBox = QListWidget(self)
75
92
  self.listBox.setDragDropMode(QAbstractItemView.NoDragDrop)
93
+ self.listBox.setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection)
76
94
  self.listBox.setSortingEnabled(True)
77
95
 
78
- self.newEntry = QLineEdit()
96
+ # Add/Remove Form
97
+ self.newEntry = QLineEdit(self)
79
98
 
80
- self.addButton = QPushButton(SHARED.theme.getIcon("add"), "")
99
+ self.addButton = QPushButton(SHARED.theme.getIcon("add"), "", self)
81
100
  self.addButton.clicked.connect(self._doAdd)
82
101
 
83
- self.delButton = QPushButton(SHARED.theme.getIcon("remove"), "")
102
+ self.delButton = QPushButton(SHARED.theme.getIcon("remove"), "", self)
84
103
  self.delButton.clicked.connect(self._doDelete)
85
104
 
86
105
  self.editBox = QHBoxLayout()
@@ -88,20 +107,19 @@ class GuiWordList(QDialog):
88
107
  self.editBox.addWidget(self.addButton, 0)
89
108
  self.editBox.addWidget(self.delButton, 0)
90
109
 
110
+ # Buttons
91
111
  self.buttonBox = QDialogButtonBox(QDialogButtonBox.Save | QDialogButtonBox.Close)
92
112
  self.buttonBox.accepted.connect(self._doSave)
93
113
  self.buttonBox.rejected.connect(self.close)
94
114
 
95
115
  # Assemble
96
- # ========
97
-
98
116
  self.outerBox = QVBoxLayout()
99
- self.outerBox.addWidget(self.headLabel)
100
- self.outerBox.addSpacing(CONFIG.pxInt(8))
117
+ self.outerBox.addLayout(self.headerBox, 0)
101
118
  self.outerBox.addWidget(self.listBox, 1)
102
119
  self.outerBox.addLayout(self.editBox, 0)
103
120
  self.outerBox.addSpacing(CONFIG.pxInt(12))
104
121
  self.outerBox.addWidget(self.buttonBox, 0)
122
+ self.outerBox.setSpacing(CONFIG.pxInt(4))
105
123
 
106
124
  self.setLayout(self.outerBox)
107
125
 
@@ -134,45 +152,69 @@ class GuiWordList(QDialog):
134
152
  def _doAdd(self) -> None:
135
153
  """Add a new word to the word list."""
136
154
  word = self.newEntry.text().strip()
137
- if word == "":
138
- SHARED.error(self.tr("Cannot add a blank word."))
139
- return
140
-
141
- if self.listBox.findItems(word, Qt.MatchExactly):
142
- SHARED.error(self.tr(
143
- "The word '{0}' is already in the word list."
144
- ).format(word))
145
- return
146
-
147
- self.listBox.addItem(word)
148
155
  self.newEntry.setText("")
149
-
156
+ self.listBox.clearSelection()
157
+ self._addWord(word)
158
+ if items := self.listBox.findItems(word, Qt.MatchExactly):
159
+ self.listBox.setCurrentItem(items[0])
160
+ self.listBox.scrollToItem(items[0], QAbstractItemView.ScrollHint.PositionAtCenter)
150
161
  return
151
162
 
152
163
  @pyqtSlot()
153
164
  def _doDelete(self) -> None:
154
- """Delete the selected item."""
155
- selItem = self.listBox.selectedItems()
156
- if selItem:
157
- self.listBox.takeItem(self.listBox.row(selItem[0]))
165
+ """Delete the selected items."""
166
+ for item in self.listBox.selectedItems():
167
+ self.listBox.takeItem(self.listBox.row(item))
158
168
  return
159
169
 
160
170
  @pyqtSlot()
161
171
  def _doSave(self) -> None:
162
172
  """Save the new word list and close."""
163
173
  userDict = UserDictionary(SHARED.project)
164
- for i in range(self.listBox.count()):
165
- item = self.listBox.item(i)
166
- if isinstance(item, QListWidgetItem):
167
- word = item.text().strip()
168
- if word:
169
- userDict.add(word)
174
+ for word in self._listWords():
175
+ userDict.add(word)
170
176
  userDict.save()
171
177
  self.newWordListReady.emit()
172
178
  qApp.processEvents()
173
179
  self.close()
174
180
  return
175
181
 
182
+ @pyqtSlot()
183
+ def _importWords(self) -> None:
184
+ """Import words from file."""
185
+ SHARED.info(self.tr(
186
+ "Note: The import file must be a plain text file with UTF-8 or ASCII encoding."
187
+ ))
188
+ ffilter = formatFileFilter(["*.txt", "*"])
189
+ path, _ = QFileDialog.getOpenFileName(
190
+ self, self.tr("Import File"), str(Path.home()), filter=ffilter
191
+ )
192
+ if path:
193
+ try:
194
+ with open(path, mode="r", encoding="utf-8") as fo:
195
+ words = set(w.strip() for w in fo.read().split())
196
+ except Exception as exc:
197
+ SHARED.error("Could not read file.", exc=exc)
198
+ return
199
+ for word in words:
200
+ self._addWord(word)
201
+ return
202
+
203
+ @pyqtSlot()
204
+ def _exportWords(self) -> None:
205
+ """Export words to file."""
206
+ path, _ = QFileDialog.getSaveFileName(
207
+ self, self.tr("Export File"), str(Path.home())
208
+ )
209
+ if path:
210
+ try:
211
+ path = Path(path).with_suffix(".txt")
212
+ with open(path, mode="w", encoding="utf-8") as fo:
213
+ fo.write("\n".join(self._listWords()))
214
+ except Exception as exc:
215
+ SHARED.error("Could not write file.", exc=exc)
216
+ return
217
+
176
218
  ##
177
219
  # Internal Functions
178
220
  ##
@@ -183,8 +225,7 @@ class GuiWordList(QDialog):
183
225
  userDict.load()
184
226
  self.listBox.clear()
185
227
  for word in userDict:
186
- if word:
187
- self.listBox.addItem(word)
228
+ self.listBox.addItem(word)
188
229
  return
189
230
 
190
231
  def _saveGuiSettings(self) -> None:
@@ -199,4 +240,19 @@ class GuiWordList(QDialog):
199
240
 
200
241
  return
201
242
 
243
+ def _addWord(self, word: str) -> None:
244
+ """Add a single word to the list."""
245
+ if word and not self.listBox.findItems(word, Qt.MatchExactly):
246
+ self.listBox.addItem(word)
247
+ self._changed = True
248
+ return
249
+
250
+ def _listWords(self) -> list[str]:
251
+ """List all words in the list box."""
252
+ result = []
253
+ for i in range(self.listBox.count()):
254
+ if (item := self.listBox.item(i)) and (word := item.text().strip()):
255
+ result.append(word)
256
+ return result
257
+
202
258
  # END Class GuiWordList
novelwriter/enum.py CHANGED
@@ -47,7 +47,8 @@ class nwItemClass(Enum):
47
47
  ENTITY = 7
48
48
  CUSTOM = 8
49
49
  ARCHIVE = 9
50
- TRASH = 10
50
+ TEMPLATE = 10
51
+ TRASH = 11
51
52
 
52
53
  # END Enum nwItemClass
53
54
 
@@ -107,23 +108,24 @@ class nwDocAction(Enum):
107
108
  BLOCK_H3 = 15
108
109
  BLOCK_H4 = 16
109
110
  BLOCK_COM = 17
110
- BLOCK_TXT = 18
111
- BLOCK_TTL = 19
112
- BLOCK_UNN = 20
113
- REPL_SNG = 21
114
- REPL_DBL = 22
115
- RM_BREAKS = 23
116
- ALIGN_L = 24
117
- ALIGN_C = 25
118
- ALIGN_R = 26
119
- INDENT_L = 27
120
- INDENT_R = 28
121
- SC_ITALIC = 29
122
- SC_BOLD = 30
123
- SC_STRIKE = 31
124
- SC_ULINE = 32
125
- SC_SUP = 33
126
- SC_SUB = 34
111
+ BLOCK_IGN = 18
112
+ BLOCK_TXT = 19
113
+ BLOCK_TTL = 20
114
+ BLOCK_UNN = 21
115
+ REPL_SNG = 22
116
+ REPL_DBL = 23
117
+ RM_BREAKS = 24
118
+ ALIGN_L = 25
119
+ ALIGN_C = 26
120
+ ALIGN_R = 27
121
+ INDENT_L = 28
122
+ INDENT_R = 29
123
+ SC_ITALIC = 30
124
+ SC_BOLD = 31
125
+ SC_STRIKE = 32
126
+ SC_ULINE = 33
127
+ SC_SUP = 34
128
+ SC_SUB = 35
127
129
 
128
130
  # END Enum nwDocAction
129
131
 
novelwriter/error.py CHANGED
@@ -50,7 +50,7 @@ def logException() -> None:
50
50
  """Log the content of an exception message."""
51
51
  exType, exValue, _ = sys.exc_info()
52
52
  if exType is not None:
53
- logger.error("%s: %s", exType.__name__, str(exValue))
53
+ logger.error(f"{exType.__name__}: {str(exValue)}", stacklevel=2)
54
54
  return
55
55
 
56
56
 
@@ -84,17 +84,17 @@ class NProgressCircle(QProgressBar):
84
84
  """Custom painter for the progress bar."""
85
85
  progress = 100.0*self.value()/self.maximum()
86
86
  angle = ceil(16*3.6*progress)
87
- qPaint = QPainter(self)
88
- qPaint.setRenderHint(QPainter.Antialiasing, True)
89
- qPaint.setPen(self._dPen)
90
- qPaint.setBrush(self._dBrush)
91
- qPaint.drawEllipse(self._dRect)
92
- qPaint.setPen(self._bPen)
93
- qPaint.drawArc(self._cRect, 0, 360*16)
94
- qPaint.setPen(self._cPen)
95
- qPaint.drawArc(self._cRect, 90*16, -angle)
96
- qPaint.setPen(self._tColor)
97
- qPaint.drawText(self._cRect, Qt.AlignCenter, self._text or f"{progress:.1f} %")
87
+ painter = QPainter(self)
88
+ painter.setRenderHint(QPainter.Antialiasing, True)
89
+ painter.setPen(self._dPen)
90
+ painter.setBrush(self._dBrush)
91
+ painter.drawEllipse(self._dRect)
92
+ painter.setPen(self._bPen)
93
+ painter.drawArc(self._cRect, 0, 360*16)
94
+ painter.setPen(self._cPen)
95
+ painter.drawArc(self._cRect, 90*16, -angle)
96
+ painter.setPen(self._tColor)
97
+ painter.drawText(self._cRect, Qt.AlignCenter, self._text or f"{progress:.1f} %")
98
98
  return
99
99
 
100
100
  # END Class NProgressCircle