Add pre-calculation of status icons

This commit is contained in:
Veronica Berglyd Olsen
2024-04-11 19:52:54 +02:00
parent 6cf83bb381
commit f67d2c5a4c
7 changed files with 120 additions and 120 deletions
+5 -4
View File
@@ -270,16 +270,17 @@ class nwLabels:
}
STATUS_SHAPES = {
nwStatusShape.SQUARE: QT_TRANSLATE_NOOP("Constant", "Square"),
nwStatusShape.CIRCLE_Q: QT_TRANSLATE_NOOP("Constant", "Circle, 1/4"),
nwStatusShape.CIRCLE_H: QT_TRANSLATE_NOOP("Constant", "Circle, Half"),
nwStatusShape.CIRCLE_T: QT_TRANSLATE_NOOP("Constant", "Circle, 3/4"),
nwStatusShape.CIRCLE: QT_TRANSLATE_NOOP("Constant", "Circle, Full"),
nwStatusShape.TRIANGLE: QT_TRANSLATE_NOOP("Constant", "Triangle"),
nwStatusShape.NABLA: QT_TRANSLATE_NOOP("Constant", "Nabla"),
nwStatusShape.DIAMOND: QT_TRANSLATE_NOOP("Constant", "Diamond"),
nwStatusShape.PENTAGON: QT_TRANSLATE_NOOP("Constant", "Pentagon"),
nwStatusShape.HEXAGON: QT_TRANSLATE_NOOP("Constant", "Hexagon"),
nwStatusShape.STAR: QT_TRANSLATE_NOOP("Constant", "Star"),
nwStatusShape.PACMAN: QT_TRANSLATE_NOOP("Constant", "Pacman"),
nwStatusShape.CIRCLE_Q: QT_TRANSLATE_NOOP("Constant", "Circle, 1/4"),
nwStatusShape.CIRCLE_H: QT_TRANSLATE_NOOP("Constant", "Circle, Half"),
nwStatusShape.CIRCLE_T: QT_TRANSLATE_NOOP("Constant", "Circle, 3/4"),
nwStatusShape.CIRCLE: QT_TRANSLATE_NOOP("Constant", "Circle, Full"),
nwStatusShape.BARS_1: QT_TRANSLATE_NOOP("Constant", "1 Bar"),
nwStatusShape.BARS_2: QT_TRANSLATE_NOOP("Constant", "2 Bars"),
nwStatusShape.BARS_3: QT_TRANSLATE_NOOP("Constant", "3 Bars"),
+57 -63
View File
@@ -29,8 +29,7 @@ import logging
import random
from collections.abc import Iterable
from math import cos, pi, sin
from typing import TYPE_CHECKING, Literal
from typing import TYPE_CHECKING
from PyQt5.QtCore import QPointF, Qt
from PyQt5.QtGui import QIcon, QPainter, QPainterPath, QPixmap, QColor, QPolygonF
@@ -71,24 +70,16 @@ NO_ENTRY = StatusEntry("", QColor(0, 0, 0), nwStatusShape.SQUARE, QIcon(), 0)
class NWStatus:
STATUS = 1
IMPORT = 2
STATUS = "s"
IMPORT = "i"
def __init__(self, kind: Literal[1, 2]) -> None:
__slots__ = ("_store", "_default", "_prefix", "_height")
self._type = kind
def __init__(self, prefix: str) -> None:
self._store: dict[str, StatusEntry] = {}
self._default = None
self._iPx = SHARED.theme.baseIconHeight
if self._type == self.STATUS:
self._prefix = "s"
elif self._type == self.IMPORT:
self._prefix = "i"
else:
raise Exception("This is a bug!")
self._prefix = prefix[:1]
self._height = SHARED.theme.baseIconHeight
return
def __len__(self) -> int:
@@ -123,7 +114,7 @@ class NWStatus:
key = self._checkKey(key)
name = simplified(name)
icon = self.createIcon(self._iPx, qColor, iShape)
icon = self.createIcon(self._height, qColor, iShape)
self._store[key] = StatusEntry(name, qColor, iShape, icon, count)
if self._default is None:
@@ -181,14 +172,14 @@ class NWStatus:
yield from self._store.items()
@staticmethod
def createIcon(height: int, colour: QColor, shape: nwStatusShape) -> QIcon:
def createIcon(height: int, color: QColor, shape: nwStatusShape) -> QIcon:
"""Generate an icon for a status label."""
pixmap = QPixmap(48, 48)
pixmap.fill(QtTransparent)
painter = QPainter(pixmap)
painter.setRenderHint(QtPaintAnitAlias)
painter.fillPath(_SHAPES.getShape(shape), colour)
painter.fillPath(_SHAPES.getShape(shape), color)
painter.end()
return QIcon(pixmap.scaled(
@@ -244,14 +235,56 @@ class _ShapeCache:
if shape in self._cache:
return self._cache[shape]
def polar(r: float, a: float, x: float, y: float) -> QPointF:
# Converts polar coordinates to cartesian
# print(round(x+r*sin(pi*a/180), 2), round(y-r*cos(pi*a/180), 2))
return QPointF(x+r*sin(pi*a/180), y-r*cos(pi*a/180))
path = QPainterPath()
if shape == nwStatusShape.SQUARE:
path.addRoundedRect(2.0, 2.0, 44.0, 44.0, 4.0, 4.0)
elif shape == nwStatusShape.TRIANGLE:
path.addPolygon(QPolygonF([
QPointF(24.00, 3.00),
QPointF(43.92, 37.50),
QPointF(4.08, 37.50),
]))
elif shape == nwStatusShape.NABLA:
path.addPolygon(QPolygonF([
QPointF(24.00, 48.00),
QPointF(4.08, 14.50),
QPointF(43.92, 14.50),
]))
elif shape == nwStatusShape.DIAMOND:
path.addPolygon(QPolygonF([
QPointF(24.00, 2.00),
QPointF(44.00, 24.00),
QPointF(24.00, 46.00),
QPointF(4.00, 24.00),
]))
elif shape == nwStatusShape.PENTAGON:
path.addPolygon(QPolygonF([
QPointF(24.00, 1.50),
QPointF(45.87, 17.39),
QPointF(37.52, 43.11),
QPointF(10.48, 43.11),
QPointF(2.13, 17.39),
]))
elif shape == nwStatusShape.HEXAGON:
path.addPolygon(QPolygonF([
QPointF(24.00, 1.50),
QPointF(43.92, 13.00),
QPointF(43.92, 36.00),
QPointF(24.00, 47.50),
QPointF(4.08, 36.00),
QPointF(4.08, 13.00),
]))
elif shape == nwStatusShape.STAR:
path.addPolygon(QPolygonF([
QPointF(24.00, 0.50), QPointF(31.05, 14.79),
QPointF(46.83, 17.08), QPointF(35.41, 28.21),
QPointF(38.11, 43.92), QPointF(24.00, 36.50),
QPointF(9.89, 43.92), QPointF(12.59, 28.21),
QPointF(1.17, 17.08), QPointF(15.37, 16.16),
]))
elif shape == nwStatusShape.PACMAN:
path.moveTo(24.0, 24.0)
path.arcTo(2.0, 2.0, 44.0, 44.0, 40.0, 280.0)
elif shape == nwStatusShape.CIRCLE_Q:
path.moveTo(24.0, 24.0)
path.arcTo(2.0, 2.0, 44.0, 44.0, 0.0, 90.0)
@@ -263,45 +296,6 @@ class _ShapeCache:
path.arcTo(2.0, 2.0, 44.0, 44.0, -180.0, 270.0)
elif shape == nwStatusShape.CIRCLE:
path.addEllipse(2.0, 2.0, 44.0, 44.0)
elif shape == nwStatusShape.TRIANGLE:
path.addPolygon(QPolygonF([
polar(23.0, 0.0, 24.0, 26.0),
polar(23.0, 120.0, 24.0, 26.0),
polar(23.0, 240.0, 24.0, 26.0),
]))
elif shape == nwStatusShape.NABLA:
path.addPolygon(QPolygonF([
polar(23.0, 180.0, 24.0, 26.0),
polar(23.0, 300.0, 24.0, 26.0),
polar(23.0, 60.0, 24.0, 26.0),
]))
elif shape == nwStatusShape.DIAMOND:
path.addPolygon(QPolygonF([
polar(22.0, 0.0, 24.0, 24.0),
polar(20.0, 90.0, 24.0, 24.0),
polar(22.0, 180.0, 24.0, 24.0),
polar(20.0, 270.0, 24.0, 24.0),
]))
elif shape == nwStatusShape.PENTAGON:
path.addPolygon(QPolygonF([
polar(23.0, 0.0, 24.0, 24.5),
polar(23.0, 72.0, 24.0, 24.5),
polar(23.0, 144.0, 24.0, 24.5),
polar(23.0, 216.0, 24.0, 24.5),
polar(23.0, 288.0, 24.0, 24.5),
]))
elif shape == nwStatusShape.STAR:
path.addPolygon(QPolygonF([
polar(24.0, 0.0, 24.0, 24.5), polar(12.0, 36.0, 24.0, 24.5),
polar(24.0, 72.0, 24.0, 24.5), polar(12.0, 108.0, 24.0, 24.5),
polar(24.0, 144.0, 24.0, 24.5), polar(12.0, 180.0, 24.0, 24.5),
polar(24.0, 216.0, 24.0, 24.5), polar(12.0, 252.0, 24.0, 24.5),
polar(24.0, 288.0, 24.0, 24.5), polar(12.0, 314.0, 24.0, 24.5),
]))
path.setFillRule(Qt.FillRule.WindingFill)
elif shape == nwStatusShape.PACMAN:
path.moveTo(24.0, 24.0)
path.arcTo(2.0, 2.0, 44.0, 44.0, 40.0, 280.0)
elif shape == nwStatusShape.BARS_1:
path.addRoundedRect(2.0, 2.0, 8.0, 44.0, 4.0, 4.0)
elif shape == nwStatusShape.BARS_2:
+15 -11
View File
@@ -334,6 +334,8 @@ class _StatusPage(NFixedPage):
iSz = SHARED.theme.baseIconSize
bSz = SHARED.theme.buttonIconSize
iColor = self.palette().text().color()
# Labels
self.trCountNone = self.tr("Not in use")
self.trCountOne = self.tr("Used once")
@@ -385,13 +387,15 @@ class _StatusPage(NFixedPage):
self.shapeList = NComboBox(self)
self.shapeList.setEnabled(False)
self.shapeList.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.MinimumExpanding)
self.shapeList.setMaxVisibleItems(5)
for shape, label in nwLabels.STATUS_SHAPES.items():
self.shapeList.addItem(label, shape)
icon = NWStatus.createIcon(self._iPx, iColor, shape)
self.shapeList.addItem(icon, label, shape)
self.saveButton = QPushButton(self.tr("Save"), self)
self.saveButton.setEnabled(False)
self.saveButton.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.MinimumExpanding)
self.saveButton.clicked.connect(self._saveItem)
self.applyButton = QPushButton(self.tr("Apply"), self)
self.applyButton.setEnabled(False)
self.applyButton.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.MinimumExpanding)
self.applyButton.clicked.connect(self._saveItem)
# Assemble
self.listControls = QVBoxLayout()
@@ -405,7 +409,7 @@ class _StatusPage(NFixedPage):
self.editBox.addWidget(self.editName, 1)
self.editBox.addWidget(self.colButton, 0)
self.editBox.addWidget(self.shapeList, 0)
self.editBox.addWidget(self.saveButton, 0)
self.editBox.addWidget(self.applyButton, 0)
self.mainBox = QVBoxLayout()
self.mainBox.addWidget(self.listBox, 1)
@@ -524,7 +528,7 @@ class _StatusPage(NFixedPage):
self.editName.setEnabled(True)
self.colButton.setEnabled(True)
self.shapeList.setEnabled(True)
self.saveButton.setEnabled(True)
self.applyButton.setEnabled(True)
else:
self._color = QColor(100, 100, 100)
@@ -535,7 +539,7 @@ class _StatusPage(NFixedPage):
self.editName.setEnabled(False)
self.colButton.setEnabled(False)
self.shapeList.setEnabled(False)
self.saveButton.setEnabled(False)
self.applyButton.setEnabled(False)
return
##
@@ -645,8 +649,8 @@ class _ReplacePage(NFixedPage):
self.editValue.setEnabled(False)
self.editValue.setMaxLength(80)
self.saveButton = QPushButton(self.tr("Save"), self)
self.saveButton.clicked.connect(self._saveEntry)
self.applyButton = QPushButton(self.tr("Apply"), self)
self.applyButton.clicked.connect(self._saveEntry)
# Assemble
self.listControls = QVBoxLayout()
@@ -657,7 +661,7 @@ class _ReplacePage(NFixedPage):
self.editBox = QHBoxLayout()
self.editBox.addWidget(self.editKey, 4)
self.editBox.addWidget(self.editValue, 5)
self.editBox.addWidget(self.saveButton, 0)
self.editBox.addWidget(self.applyButton, 0)
self.mainBox = QVBoxLayout()
self.mainBox.addWidget(self.listBox)
+19 -18
View File
@@ -210,23 +210,24 @@ class nwBuildFmt(Enum):
class nwStatusShape(Enum):
SQUARE = 0
CIRCLE_Q = 1
CIRCLE_H = 2
CIRCLE_T = 3
CIRCLE = 4
TRIANGLE = 5
NABLA = 6
DIAMOND = 7
PENTAGON = 8
STAR = 9
PACMAN = 10
BARS_1 = 11
BARS_2 = 12
BARS_3 = 13
BARS_4 = 14
BLOCK_1 = 15
BLOCK_2 = 16
BLOCK_3 = 17
BLOCK_4 = 18
TRIANGLE = 1
NABLA = 2
DIAMOND = 3
PENTAGON = 4
HEXAGON = 5
STAR = 6
PACMAN = 7
CIRCLE_Q = 8
CIRCLE_H = 9
CIRCLE_T = 10
CIRCLE = 11
BARS_1 = 12
BARS_2 = 13
BARS_3 = 14
BARS_4 = 15
BLOCK_1 = 16
BLOCK_2 = 17
BLOCK_3 = 18
BLOCK_4 = 19
# END Enum nwStatusShape
+19 -18
View File
@@ -1,6 +1,6 @@
<?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="2.4rc1" hexVersion="0x020400c1" fileVersion="1.5" fileRevision="4" timeStamp="2024-04-10 23:22:12">
<project id="e2be99af-f9bf-4403-857a-c3d1ac25abea" saveCount="1830" autoCount="265" editTime="85357">
<novelWriterXML appVersion="2.4rc1" hexVersion="0x020400c1" fileVersion="1.5" fileRevision="4" timeStamp="2024-04-11 14:19:47">
<project id="e2be99af-f9bf-4403-857a-c3d1ac25abea" saveCount="1850" autoCount="272" editTime="86438">
<name>Sample Project</name>
<author>Jane Smith</author>
</project>
@@ -22,17 +22,18 @@
<status>
<entry key="sf12341" count="8" red="100" green="100" blue="100" shape="SQUARE">New</entry>
<entry key="sf24ce6" count="2" red="200" green="50" blue="0" shape="SQUARE">Notes</entry>
<entry key="sc24b8f" count="3" red="182" green="60" blue="0" shape="SQUARE">Started</entry>
<entry key="s90e6c9" count="7" red="193" green="129" blue="0" shape="SQUARE">1st Draft</entry>
<entry key="sd51c5b" count="0" red="193" green="129" blue="0" shape="SQUARE">2nd Draft</entry>
<entry key="s8ae72a" count="0" red="193" green="129" blue="0" shape="SQUARE">3rd Draft</entry>
<entry key="sc24b8f" count="3" red="182" green="60" blue="0" shape="BARS_1">Started</entry>
<entry key="s90e6c9" count="5" red="193" green="129" blue="0" shape="BARS_2">1st Draft</entry>
<entry key="sd51c5b" count="1" red="193" green="129" blue="0" shape="BARS_3">2nd Draft</entry>
<entry key="s8ae72a" count="1" red="193" green="129" blue="0" shape="BARS_4">3rd Draft</entry>
<entry key="s78ea90" count="1" red="58" green="180" blue="58" shape="STAR">Finished</entry>
</status>
<importance>
<entry key="ia857f0" count="5" red="100" green="100" blue="100" shape="CIRCLE">None</entry>
<entry key="icfb3a5" count="2" red="0" green="122" blue="188" shape="CIRCLE">Minor</entry>
<entry key="i2d7a54" count="2" red="21" green="0" blue="180" shape="CIRCLE">Major</entry>
<entry key="i56be10" count="1" red="117" green="0" blue="175" shape="CIRCLE">Main</entry>
<entry key="ia857f0" count="5" red="100" green="100" blue="100" shape="SQUARE">None</entry>
<entry key="i4a1d39" count="1" red="220" green="138" blue="221" shape="BLOCK_1">Background</entry>
<entry key="icfb3a5" count="1" red="220" green="138" blue="221" shape="BLOCK_2">Minor</entry>
<entry key="i2d7a54" count="2" red="220" green="138" blue="221" shape="BLOCK_3">Major</entry>
<entry key="i56be10" count="1" red="220" green="138" blue="221" shape="BLOCK_4">Main</entry>
</importance>
</settings>
<content items="31" novelWords="998" notesWords="416">
@@ -57,7 +58,7 @@
<name status="sf24ce6" import="ia857f0" active="yes">Chapter One</name>
</item>
<item handle="636b6aa9b697b" parent="6a2d6d5f4f401" root="7031beac91f75" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="no" heading="H3" charCount="2937" wordCount="520" paraCount="15" cursorPos="1465" />
<meta expanded="no" heading="H3" charCount="2937" wordCount="520" paraCount="15" cursorPos="0" />
<name status="s90e6c9" import="ia857f0" active="yes">Making a Scene</name>
</item>
<item handle="bc0cbd2a407f3" parent="6a2d6d5f4f401" root="7031beac91f75" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
@@ -78,7 +79,7 @@
</item>
<item handle="ae7339df26ded" parent="88706ddc78b1b" root="7031beac91f75" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="no" heading="H3" charCount="189" wordCount="37" paraCount="1" cursorPos="237" />
<name status="s90e6c9" import="ia857f0" active="yes">We Found John!</name>
<name status="sd51c5b" import="ia857f0" active="yes">We Found John!</name>
</item>
<item handle="e5e47ebf63b1c" parent="None" root="e5e47ebf63b1c" order="1" type="ROOT" class="NOVEL">
<meta expanded="yes" />
@@ -90,7 +91,7 @@
</item>
<item handle="a520879ca0b45" parent="e5e47ebf63b1c" root="e5e47ebf63b1c" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="no" heading="H2" charCount="299" wordCount="55" paraCount="2" cursorPos="387" />
<name status="s90e6c9" import="ia857f0" active="yes">Chapter One</name>
<name status="s8ae72a" import="ia857f0" active="yes">Chapter One</name>
</item>
<item handle="f6622b4617424" parent="None" root="f6622b4617424" order="2" type="ROOT" class="CHARACTER">
<meta expanded="yes" />
@@ -102,11 +103,11 @@
</item>
<item handle="14298de4d9524" parent="f7e2d9f330615" root="f6622b4617424" order="0" type="FILE" class="CHARACTER" layout="NOTE">
<meta expanded="no" heading="H1" charCount="49" wordCount="9" paraCount="1" cursorPos="15" />
<name status="sf12341" import="icfb3a5" active="yes">John Smith</name>
<name status="sf12341" import="i2d7a54" active="yes">John Smith</name>
</item>
<item handle="bb2c23b3c42cc" parent="f7e2d9f330615" root="f6622b4617424" order="1" type="FILE" class="CHARACTER" layout="NOTE">
<meta expanded="no" heading="H1" charCount="55" wordCount="9" paraCount="1" cursorPos="31" />
<name status="sf12341" import="i2d7a54" active="yes">Jane Smith</name>
<name status="sf12341" import="i56be10" active="yes">Jane Smith</name>
</item>
<item handle="15c4492bd5107" parent="None" root="15c4492bd5107" order="3" type="ROOT" class="WORLD">
<meta expanded="yes" />
@@ -114,15 +115,15 @@
</item>
<item handle="b3e74dbc1f584" parent="15c4492bd5107" root="15c4492bd5107" order="0" type="FILE" class="WORLD" layout="NOTE">
<meta expanded="no" heading="H1" charCount="76" wordCount="15" paraCount="1" cursorPos="111" />
<name status="sf12341" import="i56be10" active="yes">Earth</name>
<name status="sf12341" import="i2d7a54" active="yes">Earth</name>
</item>
<item handle="f1471bef9f2ae" parent="15c4492bd5107" root="15c4492bd5107" order="1" type="FILE" class="WORLD" layout="NOTE">
<meta expanded="no" heading="H1" charCount="115" wordCount="24" paraCount="1" cursorPos="135" />
<name status="sf12341" import="icfb3a5" active="yes">Space</name>
<name status="sf12341" import="i4a1d39" active="yes">Space</name>
</item>
<item handle="5eaea4e8cdee8" parent="15c4492bd5107" root="15c4492bd5107" order="2" type="FILE" class="WORLD" layout="NOTE">
<meta expanded="no" heading="H1" charCount="28" wordCount="6" paraCount="1" cursorPos="62" />
<name status="sf12341" import="i2d7a54" active="yes">Mars</name>
<name status="sf12341" import="icfb3a5" active="yes">Mars</name>
</item>
<item handle="6827118336ac1" parent="None" root="6827118336ac1" order="4" type="ROOT" class="ARCHIVE">
<meta expanded="yes" />
+2 -3
View File
@@ -68,9 +68,6 @@ def testCoreStatus_Internal(mockGUI, mockRnd):
nStatus = NWStatus(NWStatus.STATUS)
nImport = NWStatus(NWStatus.IMPORT)
with pytest.raises(Exception):
NWStatus(999) # type: ignore
# Generate Key
# ============
@@ -383,6 +380,7 @@ def testCoreStatus_ShapeCache():
nabla = shapes.getShape(nwStatusShape.NABLA)
diamond = shapes.getShape(nwStatusShape.DIAMOND)
pentagon = shapes.getShape(nwStatusShape.PENTAGON)
hexagon = shapes.getShape(nwStatusShape.HEXAGON)
star = shapes.getShape(nwStatusShape.STAR)
pacman = shapes.getShape(nwStatusShape.PACMAN)
bars1 = shapes.getShape(nwStatusShape.BARS_1)
@@ -404,6 +402,7 @@ def testCoreStatus_ShapeCache():
assert shapes.getShape(nwStatusShape.NABLA) is nabla
assert shapes.getShape(nwStatusShape.DIAMOND) is diamond
assert shapes.getShape(nwStatusShape.PENTAGON) is pentagon
assert shapes.getShape(nwStatusShape.HEXAGON) is hexagon
assert shapes.getShape(nwStatusShape.STAR) is star
assert shapes.getShape(nwStatusShape.PACMAN) is pacman
assert shapes.getShape(nwStatusShape.BARS_1) is bars1
@@ -205,7 +205,7 @@ def testDlgProjSettings_StatusImport(qtbot, monkeypatch, nwGUI, projPath, mockRn
status.editName.setText("Final")
status.shapeList.setCurrentData(nwStatusShape.CIRCLE, nwStatusShape.SQUARE)
status.colButton.click()
status.saveButton.click()
status.applyButton.click()
assert status.listBox.topLevelItemCount() == 4
assert status.changed is True
@@ -281,7 +281,7 @@ def testDlgProjSettings_StatusImport(qtbot, monkeypatch, nwGUI, projPath, mockRn
importance.editName.setText("Final")
importance.shapeList.setCurrentData(nwStatusShape.TRIANGLE, nwStatusShape.SQUARE)
qtbot.mouseClick(importance.colButton, QtMouseLeft)
qtbot.mouseClick(importance.saveButton, QtMouseLeft)
qtbot.mouseClick(importance.applyButton, QtMouseLeft)
assert importance.listBox.topLevelItemCount() == 4
assert importance.changed is True
@@ -375,7 +375,7 @@ def testDlgProjSettings_Replace(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
replace.editValue.setText("")
for c in "With This Stuff ":
qtbot.keyClick(replace.editValue, c, delay=KEY_DELAY)
qtbot.mouseClick(replace.saveButton, QtMouseLeft)
qtbot.mouseClick(replace.applyButton, QtMouseLeft)
assert replace.listBox.topLevelItem(2).text(0) == "<This>" # type: ignore
assert replace.listBox.topLevelItem(2).text(1) == "With This Stuff " # type: ignore