Add Import/export of status labels (#2152)
This commit is contained in:
@@ -29,7 +29,8 @@ import uuid
|
|||||||
from typing import TYPE_CHECKING, Any
|
from typing import TYPE_CHECKING, Any
|
||||||
|
|
||||||
from novelwriter.common import (
|
from novelwriter.common import (
|
||||||
checkBool, checkInt, checkStringNone, checkUuid, isHandle, simplified
|
checkBool, checkInt, checkStringNone, checkUuid, isHandle,
|
||||||
|
makeFileNameSafe, simplified
|
||||||
)
|
)
|
||||||
from novelwriter.core.status import NWStatus
|
from novelwriter.core.status import NWStatus
|
||||||
|
|
||||||
@@ -101,6 +102,11 @@ class NWProjectData:
|
|||||||
"""Return the project name."""
|
"""Return the project name."""
|
||||||
return self._name
|
return self._name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def fileSafeName(self) -> str:
|
||||||
|
"""Return the project name in a file name safe format."""
|
||||||
|
return makeFileNameSafe(self._name)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def author(self) -> str:
|
def author(self) -> str:
|
||||||
"""Return the project author."""
|
"""Return the project author."""
|
||||||
|
|||||||
@@ -174,6 +174,20 @@ class NWStatus:
|
|||||||
"""Yield entries from the status icons."""
|
"""Yield entries from the status icons."""
|
||||||
yield from self._store.items()
|
yield from self._store.items()
|
||||||
|
|
||||||
|
def fromRaw(self, data: list[str]) -> StatusEntry | None:
|
||||||
|
"""Create a StatusEntry from a list of three strings consisting
|
||||||
|
of shape, colour, and name. This entry is not automatically
|
||||||
|
added to the list of entries.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
shape = nwStatusShape[str(data[0])]
|
||||||
|
color = QColor(str(data[1]))
|
||||||
|
icon = NWStatus.createIcon(self._height, color, shape)
|
||||||
|
return StatusEntry(simplified(data[2]), color, shape, icon)
|
||||||
|
except Exception:
|
||||||
|
logger.error("Could not parse entry %s", str(data))
|
||||||
|
return None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def createIcon(height: int, color: QColor, shape: nwStatusShape) -> QIcon:
|
def createIcon(height: int, color: QColor, shape: nwStatusShape) -> QIcon:
|
||||||
"""Generate an icon for a status label."""
|
"""Generate an icon for a status label."""
|
||||||
|
|||||||
@@ -24,18 +24,21 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import csv
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot
|
from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot
|
||||||
from PyQt5.QtGui import QCloseEvent, QColor
|
from PyQt5.QtGui import QCloseEvent, QColor
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
QAbstractItemView, QApplication, QColorDialog, QDialogButtonBox,
|
QAbstractItemView, QApplication, QColorDialog, QDialogButtonBox,
|
||||||
QHBoxLayout, QLineEdit, QMenu, QStackedWidget, QTreeWidget,
|
QFileDialog, QGridLayout, QHBoxLayout, QLineEdit, QMenu, QStackedWidget,
|
||||||
QTreeWidgetItem, QVBoxLayout, QWidget
|
QTreeWidget, QTreeWidgetItem, QVBoxLayout, QWidget
|
||||||
)
|
)
|
||||||
|
|
||||||
from novelwriter import CONFIG, SHARED
|
from novelwriter import CONFIG, SHARED
|
||||||
from novelwriter.common import qtLambda, simplified
|
from novelwriter.common import formatFileFilter, qtLambda, simplified
|
||||||
from novelwriter.constants import nwLabels, trConst
|
from novelwriter.constants import nwLabels, trConst
|
||||||
from novelwriter.core.status import NWStatus, StatusEntry
|
from novelwriter.core.status import NWStatus, StatusEntry
|
||||||
from novelwriter.enum import nwStatusShape
|
from novelwriter.enum import nwStatusShape
|
||||||
@@ -310,11 +313,13 @@ class _StatusPage(NFixedPage):
|
|||||||
super().__init__(parent=parent)
|
super().__init__(parent=parent)
|
||||||
|
|
||||||
if isStatus:
|
if isStatus:
|
||||||
status = SHARED.project.data.itemStatus
|
self._kind = self.tr("Status")
|
||||||
|
self._store = SHARED.project.data.itemStatus
|
||||||
pageLabel = self.tr("Novel Document Status Levels")
|
pageLabel = self.tr("Novel Document Status Levels")
|
||||||
colSetting = "statusColW"
|
colSetting = "statusColW"
|
||||||
else:
|
else:
|
||||||
status = SHARED.project.data.itemImport
|
self._kind = self.tr("Importance")
|
||||||
|
self._store = SHARED.project.data.itemImport
|
||||||
pageLabel = self.tr("Project Note Importance Levels")
|
pageLabel = self.tr("Project Note Importance Levels")
|
||||||
colSetting = "importColW"
|
colSetting = "importColW"
|
||||||
|
|
||||||
@@ -354,21 +359,33 @@ class _StatusPage(NFixedPage):
|
|||||||
self.listBox.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)
|
self.listBox.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)
|
||||||
self.listBox.itemSelectionChanged.connect(self._onSelectionChanged)
|
self.listBox.itemSelectionChanged.connect(self._onSelectionChanged)
|
||||||
|
|
||||||
for key, entry in status.iterItems():
|
for key, entry in self._store.iterItems():
|
||||||
self._addItem(key, StatusEntry.duplicate(entry))
|
self._addItem(key, StatusEntry.duplicate(entry))
|
||||||
|
|
||||||
# List Controls
|
# List Controls
|
||||||
self.addButton = NIconToolButton(self, iSz, "add")
|
self.addButton = NIconToolButton(self, iSz, "add")
|
||||||
|
self.addButton.setToolTip(self.tr("Add Label"))
|
||||||
self.addButton.clicked.connect(self._onItemCreate)
|
self.addButton.clicked.connect(self._onItemCreate)
|
||||||
|
|
||||||
self.delButton = NIconToolButton(self, iSz, "remove")
|
self.delButton = NIconToolButton(self, iSz, "remove")
|
||||||
|
self.delButton.setToolTip(self.tr("Delete Label"))
|
||||||
self.delButton.clicked.connect(self._onItemDelete)
|
self.delButton.clicked.connect(self._onItemDelete)
|
||||||
|
|
||||||
self.upButton = NIconToolButton(self, iSz, "up")
|
self.upButton = NIconToolButton(self, iSz, "up")
|
||||||
|
self.upButton.setToolTip(self.tr("Move Up"))
|
||||||
self.upButton.clicked.connect(qtLambda(self._moveItem, -1))
|
self.upButton.clicked.connect(qtLambda(self._moveItem, -1))
|
||||||
|
|
||||||
self.dnButton = NIconToolButton(self, iSz, "down")
|
self.downButton = NIconToolButton(self, iSz, "down")
|
||||||
self.dnButton.clicked.connect(qtLambda(self._moveItem, 1))
|
self.downButton.setToolTip(self.tr("Move Down"))
|
||||||
|
self.downButton.clicked.connect(qtLambda(self._moveItem, 1))
|
||||||
|
|
||||||
|
self.importButton = NIconToolButton(self, iSz, "import")
|
||||||
|
self.importButton.setToolTip(self.tr("Import Labels"))
|
||||||
|
self.importButton.clicked.connect(self._importLabels)
|
||||||
|
|
||||||
|
self.exportButton = NIconToolButton(self, iSz, "export")
|
||||||
|
self.exportButton.setToolTip(self.tr("Export Labels"))
|
||||||
|
self.exportButton.clicked.connect(self._exportLabels)
|
||||||
|
|
||||||
# Edit Form
|
# Edit Form
|
||||||
self.labelText = QLineEdit(self)
|
self.labelText = QLineEdit(self)
|
||||||
@@ -414,21 +431,22 @@ class _StatusPage(NFixedPage):
|
|||||||
self.listControls.addWidget(self.addButton)
|
self.listControls.addWidget(self.addButton)
|
||||||
self.listControls.addWidget(self.delButton)
|
self.listControls.addWidget(self.delButton)
|
||||||
self.listControls.addWidget(self.upButton)
|
self.listControls.addWidget(self.upButton)
|
||||||
self.listControls.addWidget(self.dnButton)
|
self.listControls.addWidget(self.downButton)
|
||||||
self.listControls.addStretch(1)
|
self.listControls.addStretch(1)
|
||||||
|
self.listControls.addWidget(self.importButton)
|
||||||
|
self.listControls.addWidget(self.exportButton)
|
||||||
|
|
||||||
self.editBox = QHBoxLayout()
|
self.editBox = QHBoxLayout()
|
||||||
self.editBox.addWidget(self.labelText, 1)
|
self.editBox.addWidget(self.labelText, 1)
|
||||||
self.editBox.addWidget(self.colorButton, 0)
|
self.editBox.addWidget(self.colorButton, 0)
|
||||||
self.editBox.addWidget(self.shapeButton, 0)
|
self.editBox.addWidget(self.shapeButton, 0)
|
||||||
|
|
||||||
self.mainBox = QVBoxLayout()
|
self.innerBox = QGridLayout()
|
||||||
self.mainBox.addWidget(self.listBox, 1)
|
self.innerBox.addWidget(self.listBox, 0, 0)
|
||||||
self.mainBox.addLayout(self.editBox, 0)
|
self.innerBox.addLayout(self.listControls, 0, 1)
|
||||||
|
self.innerBox.addLayout(self.editBox, 1, 0)
|
||||||
self.innerBox = QHBoxLayout()
|
self.innerBox.setRowStretch(0, 1)
|
||||||
self.innerBox.addLayout(self.mainBox, 1)
|
self.innerBox.setColumnStretch(0, 1)
|
||||||
self.innerBox.addLayout(self.listControls, 0)
|
|
||||||
|
|
||||||
self.outerBox = QVBoxLayout()
|
self.outerBox = QVBoxLayout()
|
||||||
self.outerBox.addWidget(self.pageTitle, 0)
|
self.outerBox.addWidget(self.pageTitle, 0)
|
||||||
@@ -540,6 +558,42 @@ class _StatusPage(NFixedPage):
|
|||||||
self.shapeButton.setEnabled(False)
|
self.shapeButton.setEnabled(False)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def _importLabels(self) -> None:
|
||||||
|
"""Import labels from file."""
|
||||||
|
if path := QFileDialog.getOpenFileName(
|
||||||
|
self, self.tr("Import File"),
|
||||||
|
str(CONFIG.homePath()), filter=formatFileFilter(["*.csv", "*"]),
|
||||||
|
)[0]:
|
||||||
|
try:
|
||||||
|
with open(path, mode="r", encoding="utf-8") as fo:
|
||||||
|
for row in csv.reader(fo):
|
||||||
|
if entry := self._store.fromRaw(row):
|
||||||
|
self._addItem(None, entry)
|
||||||
|
except Exception as exc:
|
||||||
|
SHARED.error("Could not read file.", exc=exc)
|
||||||
|
return
|
||||||
|
return
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def _exportLabels(self) -> None:
|
||||||
|
"""Export labels to file."""
|
||||||
|
name = f"{SHARED.project.data.fileSafeName} - {self._kind}.csv"
|
||||||
|
if path := QFileDialog.getSaveFileName(
|
||||||
|
self, self.tr("Export File"), str(CONFIG.homePath() / name),
|
||||||
|
)[0]:
|
||||||
|
try:
|
||||||
|
path = Path(path).with_suffix(".csv")
|
||||||
|
with open(path, mode="w", encoding="utf-8") as fo:
|
||||||
|
writer = csv.writer(fo)
|
||||||
|
for n in range(self.listBox.topLevelItemCount()):
|
||||||
|
if item := self.listBox.topLevelItem(n):
|
||||||
|
entry: StatusEntry = item.data(self.C_DATA, self.D_ENTRY)
|
||||||
|
writer.writerow([entry.shape.name, entry.color.name(), entry.name])
|
||||||
|
except Exception as exc:
|
||||||
|
SHARED.error("Could not write file.", exc=exc)
|
||||||
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
# Internal Functions
|
# Internal Functions
|
||||||
##
|
##
|
||||||
|
|||||||
@@ -96,9 +96,11 @@ class GuiWordList(NDialog):
|
|||||||
self.newEntry = QLineEdit(self)
|
self.newEntry = QLineEdit(self)
|
||||||
|
|
||||||
self.addButton = NIconToolButton(self, iSz, "add")
|
self.addButton = NIconToolButton(self, iSz, "add")
|
||||||
|
self.addButton.setToolTip(self.tr("Add Word"))
|
||||||
self.addButton.clicked.connect(self._doAdd)
|
self.addButton.clicked.connect(self._doAdd)
|
||||||
|
|
||||||
self.delButton = NIconToolButton(self, iSz, "remove")
|
self.delButton = NIconToolButton(self, iSz, "remove")
|
||||||
|
self.delButton.setToolTip(self.tr("Remove Word"))
|
||||||
self.delButton.clicked.connect(self._doDelete)
|
self.delButton.clicked.connect(self._doDelete)
|
||||||
|
|
||||||
self.editBox = QHBoxLayout()
|
self.editBox = QHBoxLayout()
|
||||||
@@ -184,11 +186,10 @@ class GuiWordList(NDialog):
|
|||||||
SHARED.info(self.tr(
|
SHARED.info(self.tr(
|
||||||
"Note: The import file must be a plain text file with UTF-8 or ASCII encoding."
|
"Note: The import file must be a plain text file with UTF-8 or ASCII encoding."
|
||||||
))
|
))
|
||||||
ffilter = formatFileFilter(["*.txt", "*"])
|
if path := QFileDialog.getOpenFileName(
|
||||||
path, _ = QFileDialog.getOpenFileName(
|
self, self.tr("Import File"), str(CONFIG.homePath()),
|
||||||
self, self.tr("Import File"), str(CONFIG.homePath()), filter=ffilter
|
filter=formatFileFilter(["*.txt", "*"]),
|
||||||
)
|
)[0]:
|
||||||
if path:
|
|
||||||
try:
|
try:
|
||||||
with open(path, mode="r", encoding="utf-8") as fo:
|
with open(path, mode="r", encoding="utf-8") as fo:
|
||||||
words = set(w.strip() for w in fo.read().split())
|
words = set(w.strip() for w in fo.read().split())
|
||||||
@@ -202,10 +203,10 @@ class GuiWordList(NDialog):
|
|||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def _exportWords(self) -> None:
|
def _exportWords(self) -> None:
|
||||||
"""Export words to file."""
|
"""Export words to file."""
|
||||||
path, _ = QFileDialog.getSaveFileName(
|
name = f"{SHARED.project.data.fileSafeName} - {self.windowTitle()}.txt"
|
||||||
self, self.tr("Export File"), str(CONFIG.homePath())
|
if path := QFileDialog.getSaveFileName(
|
||||||
)
|
self, self.tr("Export File"), str(CONFIG.homePath() / name),
|
||||||
if path:
|
)[0]:
|
||||||
try:
|
try:
|
||||||
path = Path(path).with_suffix(".txt")
|
path = Path(path).with_suffix(".txt")
|
||||||
with open(path, mode="w", encoding="utf-8") as fo:
|
with open(path, mode="w", encoding="utf-8") as fo:
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ from PyQt5.QtWidgets import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
from novelwriter import CONFIG, SHARED
|
from novelwriter import CONFIG, SHARED
|
||||||
from novelwriter.common import checkInt, formatFileFilter, makeFileNameSafe
|
from novelwriter.common import checkInt, formatFileFilter
|
||||||
from novelwriter.constants import nwKeyWords, nwLabels, nwStats, nwStyles, trConst
|
from novelwriter.constants import nwKeyWords, nwLabels, nwStats, nwStyles, trConst
|
||||||
from novelwriter.enum import nwChange, nwDocMode, nwItemClass, nwItemLayout, nwItemType, nwOutline
|
from novelwriter.enum import nwChange, nwDocMode, nwItemClass, nwItemLayout, nwItemType, nwOutline
|
||||||
from novelwriter.error import logException
|
from novelwriter.error import logException
|
||||||
@@ -541,11 +541,10 @@ class GuiOutlineTree(QTreeWidget):
|
|||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def exportOutline(self) -> None:
|
def exportOutline(self) -> None:
|
||||||
"""Export the outline as a CSV file."""
|
"""Export the outline as a CSV file."""
|
||||||
path = CONFIG.lastPath("outline") / f"{makeFileNameSafe(SHARED.project.data.name)}.csv"
|
name = CONFIG.lastPath("outline") / f"{SHARED.project.data.fileSafeName}.csv"
|
||||||
path, _ = QFileDialog.getSaveFileName(
|
if path := QFileDialog.getSaveFileName(
|
||||||
self, self.tr("Save Outline As"), str(path), formatFileFilter(["*.csv", "*"])
|
self, self.tr("Save Outline As"), str(name), formatFileFilter(["*.csv", "*"])
|
||||||
)
|
)[0]:
|
||||||
if path:
|
|
||||||
CONFIG.setLastPath("outline", path)
|
CONFIG.setLastPath("outline", path)
|
||||||
logger.info("Writing CSV file: %s", path)
|
logger.info("Writing CSV file: %s", path)
|
||||||
cols = [col for col in self._treeOrder if not self._colHidden[col]]
|
cols = [col for col in self._treeOrder if not self._colHidden[col]]
|
||||||
|
|||||||
@@ -303,6 +303,20 @@ def testCoreStatus_Entries(mockGUI, mockRnd):
|
|||||||
assert list(nStatus._store.keys()) == [statusKeys[3], statusKeys[2]]
|
assert list(nStatus._store.keys()) == [statusKeys[3], statusKeys[2]]
|
||||||
assert nStatus._default == statusKeys[3]
|
assert nStatus._default == statusKeys[3]
|
||||||
|
|
||||||
|
# Create
|
||||||
|
# ======
|
||||||
|
|
||||||
|
# A valid entry
|
||||||
|
entry = nStatus.fromRaw(["STAR", "#ff7f00", "Test"])
|
||||||
|
assert entry is not None
|
||||||
|
assert entry.shape == nwStatusShape.STAR
|
||||||
|
assert entry.color == QColor(255, 127, 0)
|
||||||
|
assert entry.name == "Test"
|
||||||
|
|
||||||
|
# Invalid entries
|
||||||
|
assert nStatus.fromRaw(["STAR", "#ff7f00"]) is None
|
||||||
|
assert nStatus.fromRaw(["STUFF", "#ff7f00", "Test"]) is None
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreStatus_Pack(mockGUI, mockRnd):
|
def testCoreStatus_Pack(mockGUI, mockRnd):
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ from __future__ import annotations
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PyQt5.QtGui import QColor
|
from PyQt5.QtGui import QColor
|
||||||
from PyQt5.QtWidgets import QAction, QColorDialog
|
from PyQt5.QtWidgets import QAction, QColorDialog, QFileDialog
|
||||||
|
|
||||||
from novelwriter import CONFIG, SHARED
|
from novelwriter import CONFIG, SHARED
|
||||||
from novelwriter.dialogs.editlabel import GuiEditLabel
|
from novelwriter.dialogs.editlabel import GuiEditLabel
|
||||||
@@ -31,6 +31,7 @@ from novelwriter.dialogs.projectsettings import GuiProjectSettings
|
|||||||
from novelwriter.enum import nwItemType, nwStatusShape
|
from novelwriter.enum import nwItemType, nwStatusShape
|
||||||
from novelwriter.types import QtAccepted, QtMouseLeft
|
from novelwriter.types import QtAccepted, QtMouseLeft
|
||||||
|
|
||||||
|
from tests.mocked import causeOSError
|
||||||
from tests.tools import C, buildTestProject
|
from tests.tools import C, buildTestProject
|
||||||
|
|
||||||
KEY_DELAY = 1
|
KEY_DELAY = 1
|
||||||
@@ -319,6 +320,73 @@ def testDlgProjSettings_StatusImport(qtbot, monkeypatch, nwGUI, projPath, mockRn
|
|||||||
# qtbot.stop()
|
# qtbot.stop()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.gui
|
||||||
|
def testDlgProjSettings_StatusImportExport(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
|
||||||
|
"""Test the status and importance import/export."""
|
||||||
|
buildTestProject(nwGUI, projPath)
|
||||||
|
|
||||||
|
# Create Dialog
|
||||||
|
projSettings = GuiProjectSettings(nwGUI, GuiProjectSettings.PAGE_STATUS)
|
||||||
|
projSettings.show()
|
||||||
|
qtbot.addWidget(projSettings)
|
||||||
|
|
||||||
|
status = projSettings.statusPage
|
||||||
|
assert status.listBox.topLevelItemCount() == 4
|
||||||
|
expFile = projPath / "status.csv"
|
||||||
|
|
||||||
|
# Export Error
|
||||||
|
with monkeypatch.context() as mp:
|
||||||
|
mp.setattr(QFileDialog, "getSaveFileName", lambda *a, **k: (str(expFile), ""))
|
||||||
|
mp.setattr("builtins.open", causeOSError)
|
||||||
|
status._exportLabels()
|
||||||
|
|
||||||
|
assert expFile.is_file() is False
|
||||||
|
|
||||||
|
# Export File
|
||||||
|
with monkeypatch.context() as mp:
|
||||||
|
mp.setattr(QFileDialog, "getSaveFileName", lambda *a, **k: (str(expFile), ""))
|
||||||
|
status._exportLabels()
|
||||||
|
|
||||||
|
assert expFile.is_file() is True
|
||||||
|
assert expFile.read_text().split() == [
|
||||||
|
"SQUARE,#646464,New",
|
||||||
|
"SQUARE,#c83200,Note",
|
||||||
|
"SQUARE,#c89600,Draft",
|
||||||
|
"SQUARE,#32c800,Finished",
|
||||||
|
]
|
||||||
|
|
||||||
|
# Import Error
|
||||||
|
with monkeypatch.context() as mp:
|
||||||
|
mp.setattr(QFileDialog, "getOpenFileName", lambda *a, **k: (str(expFile), ""))
|
||||||
|
mp.setattr("builtins.open", causeOSError)
|
||||||
|
status._importLabels()
|
||||||
|
|
||||||
|
assert status.listBox.topLevelItemCount() == 4
|
||||||
|
|
||||||
|
# Import File
|
||||||
|
with monkeypatch.context() as mp:
|
||||||
|
mp.setattr(QFileDialog, "getOpenFileName", lambda *a, **k: (str(expFile), ""))
|
||||||
|
status._importLabels()
|
||||||
|
|
||||||
|
assert status.listBox.topLevelItemCount() == 8
|
||||||
|
|
||||||
|
item4 = status.listBox.topLevelItem(4)
|
||||||
|
assert item4 is not None
|
||||||
|
assert item4.text(0) == "New"
|
||||||
|
|
||||||
|
item5 = status.listBox.topLevelItem(5)
|
||||||
|
assert item5 is not None
|
||||||
|
assert item5.text(0) == "Note"
|
||||||
|
|
||||||
|
item6 = status.listBox.topLevelItem(6)
|
||||||
|
assert item6 is not None
|
||||||
|
assert item6.text(0) == "Draft"
|
||||||
|
|
||||||
|
item7 = status.listBox.topLevelItem(7)
|
||||||
|
assert item7 is not None
|
||||||
|
assert item7.text(0) == "Finished"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.gui
|
@pytest.mark.gui
|
||||||
def testDlgProjSettings_Replace(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
|
def testDlgProjSettings_Replace(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
|
||||||
"""Test the auto-replace page of the dialog."""
|
"""Test the auto-replace page of the dialog."""
|
||||||
|
|||||||
Reference in New Issue
Block a user