From 6e64ede90b8b1f9dc35a2994cafb14c43db730d6 Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Fri, 12 Feb 2021 17:01:46 +0100
Subject: [PATCH 1/4] Add a starting widget for the word liste ditor and
connect it to the GUI
---
nw/core/options.py | 4 ++
nw/gui/__init__.py | 2 +
nw/gui/mainmenu.py | 13 +++--
nw/gui/wordlist.py | 142 +++++++++++++++++++++++++++++++++++++++++++++
nw/guimain.py | 18 +++++-
5 files changed, 174 insertions(+), 5 deletions(-)
create mode 100644 nw/gui/wordlist.py
diff --git a/nw/core/options.py b/nw/core/options.py
index 52a30af5..51fba3b6 100644
--- a/nw/core/options.py
+++ b/nw/core/options.py
@@ -104,6 +104,10 @@ class OptionState():
"countFrom",
"clearDouble",
},
+ "GuiWordList": {
+ "winWidth",
+ "winHeight",
+ }
}
return
diff --git a/nw/gui/__init__.py b/nw/gui/__init__.py
index aa7455b5..8744348f 100644
--- a/nw/gui/__init__.py
+++ b/nw/gui/__init__.py
@@ -20,6 +20,7 @@ from nw.gui.projtree import GuiProjectTree
from nw.gui.projwizard import GuiProjectWizard
from nw.gui.statusbar import GuiMainStatus
from nw.gui.theme import GuiTheme
+from nw.gui.wordlist import GuiWordList
from nw.gui.writingstats import GuiWritingStats
__all__ = [
@@ -44,5 +45,6 @@ __all__ = [
"GuiProjectTree",
"GuiProjectWizard",
"GuiTheme",
+ "GuiWordList",
"GuiWritingStats",
]
diff --git a/nw/gui/mainmenu.py b/nw/gui/mainmenu.py
index 4cf4960a..6d6267a9 100644
--- a/nw/gui/mainmenu.py
+++ b/nw/gui/mainmenu.py
@@ -910,23 +910,28 @@ class GuiMainMenu(QMenuBar):
# Tools
self.toolsMenu = self.addMenu("&Tools")
- # Tools > Toggle Spell Check
+ # Tools > Check Spelling
self.aSpellCheck = QAction("Check Spelling", self)
self.aSpellCheck.setStatusTip("Toggle check spelling")
self.aSpellCheck.setCheckable(True)
self.aSpellCheck.setChecked(self.theProject.spellCheck)
- # Here we must used triggered, not toggled, to avoid recursion
- self.aSpellCheck.triggered.connect(self._toggleSpellCheck)
+ self.aSpellCheck.triggered.connect(self._toggleSpellCheck) # triggered, not toggled!
self.aSpellCheck.setShortcut("Ctrl+F7")
self.toolsMenu.addAction(self.aSpellCheck)
- # Tools > Update Spell Check
+ # Tools > Re-Run Spell Check
self.aReRunSpell = QAction("Re-Run Spell Check", self)
self.aReRunSpell.setStatusTip("Run the spell checker on current document")
self.aReRunSpell.setShortcut("F7")
self.aReRunSpell.triggered.connect(lambda: self.theParent.docEditor.spellCheckDocument())
self.toolsMenu.addAction(self.aReRunSpell)
+ # Tools > Project Word List
+ self.aEditWordList = QAction("Project Word List", self)
+ self.aEditWordList.setStatusTip("Edit the project's word list")
+ self.aEditWordList.triggered.connect(lambda: self.theParent.showProjectWordListDialog())
+ self.toolsMenu.addAction(self.aEditWordList)
+
# Tools > Separator
self.toolsMenu.addSeparator()
diff --git a/nw/gui/wordlist.py b/nw/gui/wordlist.py
new file mode 100644
index 00000000..fcf8682f
--- /dev/null
+++ b/nw/gui/wordlist.py
@@ -0,0 +1,142 @@
+# -*- coding: utf-8 -*-
+"""
+novelWriter – GUI User Wordlist
+===============================
+Class holding the user's wordlist dialog
+
+File History:
+Created: 2021-02-12 [1.2b1]
+
+This file is a part of novelWriter
+Copyright 2018–2021, Veronica Berglyd Olsen
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+"""
+
+import nw
+import logging
+import os
+
+from PyQt5.QtWidgets import (
+ QDialog, QDialogButtonBox, QVBoxLayout, QListWidget, QAbstractItemView
+)
+
+from nw.constants import nwFiles
+
+logger = logging.getLogger(__name__)
+
+class GuiWordList(QDialog):
+
+ def __init__(self, theParent, theProject):
+ QDialog.__init__(self, theParent)
+
+ logger.debug("Initialising GuiWordList ...")
+ self.setObjectName("GuiWordList")
+
+ self.mainConf = nw.CONFIG
+ self.theParent = theParent
+ self.theProject = theProject
+ self.optState = theProject.optState
+
+ self.setWindowTitle("Project Word List")
+
+ wW = self.mainConf.pxInt(250)
+ wH = self.mainConf.pxInt(300)
+
+ self.setMinimumWidth(wW)
+ self.setMinimumHeight(wH)
+ self.resize(
+ self.mainConf.pxInt(self.optState.getInt("GuiWordList", "winWidth", wW)),
+ self.mainConf.pxInt(self.optState.getInt("GuiWordList", "winHeight", wH))
+ )
+
+ # Main Widgets
+ # ============
+
+ self.listBox = QListWidget()
+ self.listBox.setDragDropMode(QAbstractItemView.NoDragDrop)
+ self.listBox.setSortingEnabled(True)
+
+ self.buttonBox = QDialogButtonBox(QDialogButtonBox.Save | QDialogButtonBox.Close)
+ self.buttonBox.accepted.connect(self._doSave)
+ self.buttonBox.rejected.connect(self._doClose)
+
+ # Assemble
+ # ========
+
+ self.outerBox = QVBoxLayout()
+ self.outerBox.addWidget(self.listBox, 1)
+ self.outerBox.addWidget(self.buttonBox, 0)
+
+ self.setLayout(self.outerBox)
+
+ self._loadWordList()
+
+ logger.debug("GuiWordList initialisation complete")
+
+ return
+
+ ##
+ # Slots
+ ##
+
+ def _doSave(self):
+ """Save the new word list and close.
+ """
+ self._saveGuiSettings()
+ self.accept()
+ return
+
+ def _doClose(self):
+ """Close without saving the word list.
+ """
+ self._saveGuiSettings()
+ self.reject()
+ return
+
+ ##
+ # Internal Functions
+ ##
+
+ def _loadWordList(self):
+ """Load the project's word list, if it exists.
+ """
+ self.listBox.clear()
+
+ wordList = os.path.join(self.theProject.projMeta, nwFiles.PROJ_DICT)
+ if not os.path.isfile(wordList):
+ logger.debug("No project dictionary file found")
+ return False
+
+ with open(wordList, mode="r", encoding="utf8") as inFile:
+ for inLine in inFile:
+ theWord = inLine.strip()
+ if len(theWord) == 0:
+ continue
+ self.listBox.addItem(theWord)
+
+ return True
+
+ def _saveGuiSettings(self):
+ """Save GUI settings.
+ """
+ winWidth = self.mainConf.rpxInt(self.width())
+ winHeight = self.mainConf.rpxInt(self.height())
+
+ self.optState.setValue("GuiWordList", "winWidth", winWidth)
+ self.optState.setValue("GuiWordList", "winHeight", winHeight)
+
+ return
+
+# END Class GuiWordList
diff --git a/nw/guimain.py b/nw/guimain.py
index cb2756cc..268862b3 100644
--- a/nw/guimain.py
+++ b/nw/guimain.py
@@ -43,7 +43,7 @@ from nw.gui import (
GuiDocViewDetails, GuiDocViewer, GuiItemDetails, GuiItemEditor,
GuiMainMenu, GuiMainStatus, GuiNovelTree, GuiOutline, GuiOutlineDetails,
GuiPreferences, GuiProjectDetails, GuiProjectLoad, GuiProjectSettings,
- GuiProjectTree, GuiProjectWizard, GuiTheme, GuiWritingStats
+ GuiProjectTree, GuiProjectWizard, GuiTheme, GuiWordList, GuiWritingStats
)
from nw.core import NWProject, NWDoc, NWIndex
from nw.constants import nwItemType, nwItemClass, nwAlert, nwLists
@@ -1017,6 +1017,22 @@ class GuiMain(QMainWindow):
return
+ def showProjectWordListDialog(self):
+ """Open the project word list dialog.
+ """
+ if not self.hasProject:
+ logger.error("No project open")
+ return
+
+ dlgWords = GuiWordList(self, self.theProject)
+ dlgWords.exec_()
+
+ if dlgWords.result() == QDialog.Accepted:
+ logger.debug("Reloading word list")
+ # ToDo
+
+ return
+
def showWritingStatsDialog(self):
"""Open the session log dialog.
"""
From b115d4f02dbafa8b8830c4ac3016462e93096d93 Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Fri, 12 Feb 2021 19:40:10 +0100
Subject: [PATCH 2/4] Finish the word list dialog and make it possible to
reload the user dictionary with pyenchant
---
nw/core/spellcheck.py | 12 ++++++-
nw/gui/wordlist.py | 82 +++++++++++++++++++++++++++++++++++++++----
nw/guimain.py | 2 +-
3 files changed, 88 insertions(+), 8 deletions(-)
diff --git a/nw/core/spellcheck.py b/nw/core/spellcheck.py
index 8dd33bbe..e221f8b5 100644
--- a/nw/core/spellcheck.py
+++ b/nw/core/spellcheck.py
@@ -123,6 +123,7 @@ class NWSpellCheck():
if len(theLine) > 0 and theLine not in self.projDict:
self.projDict.append(theLine)
logger.debug("Project word list contains %d words" % len(self.projDict))
+
except Exception:
logger.error("Failed to load project word list")
nw.logException()
@@ -141,6 +142,7 @@ class NWSpellEnchant(NWSpellCheck):
def __init__(self):
NWSpellCheck.__init__(self)
logger.debug("Enchant spell checking activated")
+ self.theBroker = None
return
def setLanguage(self, theLang, projectDict=None):
@@ -150,9 +152,15 @@ class NWSpellEnchant(NWSpellCheck):
"""
try:
import enchant
- self.theDict = enchant.Dict(theLang)
+ if self.theBroker is not None:
+ logger.verbose("Deleting old pyenchant Broker")
+ del self.theBroker
+
+ self.theBroker = enchant.Broker()
+ self.theDict = self.theBroker.request_dict(theLang)
self.spellLanguage = theLang
logger.debug("Enchant spell checking for language %s loaded" % theLang)
+
except Exception:
logger.error("Failed to load enchant spell checking for language %s" % theLang)
self.theDict = NWSpellEnchantDummy()
@@ -258,9 +266,11 @@ class NWSpellSimple(NWSpellCheck):
if len(theLine) == 0 or theLine.startswith("#"):
continue
self.WORDS.append(theLine.strip().lower())
+
logger.debug("Spell check word list for language %s loaded" % theLang)
logger.debug("Word list contains %d words" % len(self.WORDS))
self.spellLanguage = theLang
+
except Exception:
logger.error("Failed to load spell check word list for language %s" % theLang)
nw.logException()
diff --git a/nw/gui/wordlist.py b/nw/gui/wordlist.py
index fcf8682f..5ce377cc 100644
--- a/nw/gui/wordlist.py
+++ b/nw/gui/wordlist.py
@@ -28,11 +28,13 @@ import nw
import logging
import os
+from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (
- QDialog, QDialogButtonBox, QVBoxLayout, QListWidget, QAbstractItemView
+ QDialog, QDialogButtonBox, QVBoxLayout, QHBoxLayout, QListWidget,
+ QAbstractItemView, QPushButton, QLineEdit, QLabel
)
-from nw.constants import nwFiles
+from nw.constants import nwFiles, nwAlert
logger = logging.getLogger(__name__)
@@ -46,16 +48,18 @@ class GuiWordList(QDialog):
self.mainConf = nw.CONFIG
self.theParent = theParent
+ self.theTheme = theParent.theTheme
self.theProject = theProject
self.optState = theProject.optState
self.setWindowTitle("Project Word List")
- wW = self.mainConf.pxInt(250)
- wH = self.mainConf.pxInt(300)
+ mS = self.mainConf.pxInt(250)
+ wW = self.mainConf.pxInt(320)
+ wH = self.mainConf.pxInt(340)
- self.setMinimumWidth(wW)
- self.setMinimumHeight(wH)
+ self.setMinimumWidth(mS)
+ self.setMinimumHeight(mS)
self.resize(
self.mainConf.pxInt(self.optState.getInt("GuiWordList", "winWidth", wW)),
self.mainConf.pxInt(self.optState.getInt("GuiWordList", "winHeight", wH))
@@ -64,10 +68,27 @@ class GuiWordList(QDialog):
# Main Widgets
# ============
+ self.headLabel = QLabel("Project Word List")
+
self.listBox = QListWidget()
self.listBox.setDragDropMode(QAbstractItemView.NoDragDrop)
self.listBox.setSortingEnabled(True)
+ self.newEntry = QLineEdit()
+
+ self.addButton = QPushButton(self.theTheme.getIcon("add"), "")
+ self.addButton.setToolTip("Add new entry")
+ self.addButton.clicked.connect(self._doAdd)
+
+ self.delButton = QPushButton(self.theTheme.getIcon("remove"), "")
+ self.delButton.setToolTip("Delete selected entry")
+ self.delButton.clicked.connect(self._doDelete)
+
+ self.editBox = QHBoxLayout()
+ self.editBox.addWidget(self.newEntry, 1)
+ self.editBox.addWidget(self.addButton, 0)
+ self.editBox.addWidget(self.delButton, 0)
+
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Save | QDialogButtonBox.Close)
self.buttonBox.accepted.connect(self._doSave)
self.buttonBox.rejected.connect(self._doClose)
@@ -76,7 +97,11 @@ class GuiWordList(QDialog):
# ========
self.outerBox = QVBoxLayout()
+ self.outerBox.addWidget(self.headLabel)
+ self.outerBox.addSpacing(self.mainConf.pxInt(8))
self.outerBox.addWidget(self.listBox, 1)
+ self.outerBox.addLayout(self.editBox, 0)
+ self.outerBox.addSpacing(self.mainConf.pxInt(12))
self.outerBox.addWidget(self.buttonBox, 0)
self.setLayout(self.outerBox)
@@ -91,11 +116,56 @@ class GuiWordList(QDialog):
# Slots
##
+ def _doAdd(self):
+ """Add a new word to the word list.
+ """
+ newWord = self.newEntry.text().strip()
+ if newWord == "":
+ self.theParent.makeAlert("Cannot add a blank word.", nwAlert.ERROR)
+ return False
+
+ if self.listBox.findItems(newWord, Qt.MatchExactly):
+ self.theParent.makeAlert(
+ "The word '%s' is already in the word list." % newWord, nwAlert.ERROR
+ )
+ return False
+
+ self.listBox.addItem(newWord)
+ self.newEntry.setText("")
+
+ return True
+
+ def _doDelete(self):
+ """Delete the selected item.
+ """
+ selItem = self.listBox.selectedItems()
+ if selItem:
+ self.listBox.takeItem(self.listBox.row(selItem[0]))
+ return
+
def _doSave(self):
"""Save the new word list and close.
"""
self._saveGuiSettings()
+
+ dctFile = os.path.join(self.theProject.projMeta, nwFiles.PROJ_DICT)
+ tmpFile = dctFile + "~"
+
+ try:
+ with open(tmpFile, mode="w", encoding="utf8") as outFile:
+ for i in range(self.listBox.count()):
+ outFile.write(self.listBox.item(i).text() + "\n")
+
+ except Exception as e:
+ logger.error("Could not save new word list")
+ logger.error(str(e))
+ self.reject()
+
+ if os.path.isfile(dctFile):
+ os.unlink(dctFile)
+ os.rename(tmpFile, dctFile)
self.accept()
+
return
def _doClose(self):
diff --git a/nw/guimain.py b/nw/guimain.py
index 268862b3..6cb68cb3 100644
--- a/nw/guimain.py
+++ b/nw/guimain.py
@@ -1029,7 +1029,7 @@ class GuiMain(QMainWindow):
if dlgWords.result() == QDialog.Accepted:
logger.debug("Reloading word list")
- # ToDo
+ self.docEditor.setDictionaries()
return
From ff1d625dc8eef15cc1ffac792717be8710c7740e Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Fri, 12 Feb 2021 20:26:37 +0100
Subject: [PATCH 3/4] Fix logging of exception
---
nw/gui/wordlist.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/nw/gui/wordlist.py b/nw/gui/wordlist.py
index 5ce377cc..0d3e3072 100644
--- a/nw/gui/wordlist.py
+++ b/nw/gui/wordlist.py
@@ -156,9 +156,9 @@ class GuiWordList(QDialog):
for i in range(self.listBox.count()):
outFile.write(self.listBox.item(i).text() + "\n")
- except Exception as e:
+ except Exception:
logger.error("Could not save new word list")
- logger.error(str(e))
+ nw.logException()
self.reject()
if os.path.isfile(dctFile):
From fca5c7708971d47a513b266764b5f593abd2ce4a Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Fri, 12 Feb 2021 22:00:22 +0100
Subject: [PATCH 4/4] Add GuiWordList test
---
nw/core/spellcheck.py | 2 +-
nw/gui/wordlist.py | 3 +-
tests/test_gui/test_gui_dialogs.py | 2 +-
tests/test_gui/test_gui_wordlist.py | 133 ++++++++++++++++++++++++++++
4 files changed, 137 insertions(+), 3 deletions(-)
create mode 100644 tests/test_gui/test_gui_wordlist.py
diff --git a/nw/core/spellcheck.py b/nw/core/spellcheck.py
index 79c2accc..9d22afcd 100644
--- a/nw/core/spellcheck.py
+++ b/nw/core/spellcheck.py
@@ -153,7 +153,7 @@ class NWSpellEnchant(NWSpellCheck):
try:
import enchant
if self.theBroker is not None:
- logger.verbose("Deleting old pyenchant Broker")
+ logger.verbose("Deleting old pyenchant broker")
del self.theBroker
self.theBroker = enchant.Broker()
diff --git a/nw/gui/wordlist.py b/nw/gui/wordlist.py
index 0d3e3072..f5b6fe80 100644
--- a/nw/gui/wordlist.py
+++ b/nw/gui/wordlist.py
@@ -160,13 +160,14 @@ class GuiWordList(QDialog):
logger.error("Could not save new word list")
nw.logException()
self.reject()
+ return False
if os.path.isfile(dctFile):
os.unlink(dctFile)
os.rename(tmpFile, dctFile)
self.accept()
- return
+ return True
def _doClose(self):
"""Close without saving the word list.
diff --git a/tests/test_gui/test_gui_dialogs.py b/tests/test_gui/test_gui_dialogs.py
index 243434d3..f4f6f27d 100644
--- a/tests/test_gui/test_gui_dialogs.py
+++ b/tests/test_gui/test_gui_dialogs.py
@@ -61,7 +61,7 @@ def testGuiDialogs_Quotes(qtbot, monkeypatch, nwGUI, nwMinimal):
# END Test testDialogs_Quotes
@pytest.mark.gui
-def testGuiDialogs_Other(qtbot, monkeypatch, nwGUI, nwMinimal, tmpDir):
+def testGuiDialogs_Other(qtbot, monkeypatch, nwGUI, tmpDir):
"""Various other dialog tests.
"""
monkeypatch.setattr(QFileDialog, "getExistingDirectory", lambda *args, **kwargs: tmpDir)
diff --git a/tests/test_gui/test_gui_wordlist.py b/tests/test_gui/test_gui_wordlist.py
new file mode 100644
index 00000000..fdbc1725
--- /dev/null
+++ b/tests/test_gui/test_gui_wordlist.py
@@ -0,0 +1,133 @@
+# -*- coding: utf-8 -*-
+"""
+novelWriter – Other Dialog Classes Tester
+=========================================
+
+This file is a part of novelWriter
+Copyright 2018–2021, Veronica Berglyd Olsen
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+"""
+
+import os
+
+from PyQt5.QtCore import Qt
+from PyQt5.QtWidgets import QDialog, QMessageBox, QAction
+
+from tools import writeFile, readFile, getGuiItem
+from dummy import causeOSError
+
+from nw.gui.wordlist import GuiWordList
+from nw.constants import nwFiles
+
+keyDelay = 2
+typeDelay = 1
+stepDelay = 20
+
+def testGuiWordList_Dialog(qtbot, monkeypatch, nwGUI, nwMinimal, tmpDir):
+ """test the word list editor.
+ """
+ monkeypatch.setattr(QMessageBox, "question", lambda *args: QMessageBox.Yes)
+ monkeypatch.setattr(QMessageBox, "critical", lambda *args: QMessageBox.Yes)
+ monkeypatch.setattr(GuiWordList, "exec_", lambda *args: None)
+ monkeypatch.setattr(GuiWordList, "result", lambda *args: QDialog.Accepted)
+ monkeypatch.setattr(GuiWordList, "accept", lambda *args: None)
+
+ # Open project
+ nwGUI.openProject(nwMinimal)
+ qtbot.wait(stepDelay)
+ dictFile = os.path.join(nwMinimal, "meta", nwFiles.PROJ_DICT)
+
+ # Load the dialog
+ nwGUI.mainMenu.aEditWordList.activate(QAction.Trigger)
+ qtbot.waitUntil(lambda: getGuiItem("GuiWordList") is not None, timeout=1000)
+
+ wList = getGuiItem("GuiWordList")
+ assert isinstance(wList, GuiWordList)
+ wList.show()
+ qtbot.wait(stepDelay)
+
+ # List should be blank
+ assert wList.listBox.count() == 0
+
+ # Add words
+ writeFile(dictFile, (
+ "word_a\n"
+ "word_c\n"
+ "word_g\n"
+ " \n" # Should be ignored
+ "word_f\n"
+ "word_b\n"
+ ))
+ qtbot.wait(stepDelay)
+ assert wList._loadWordList()
+
+ # Check that the content was loaded
+ assert wList.listBox.item(0).text() == "word_a"
+ assert wList.listBox.item(1).text() == "word_b"
+ assert wList.listBox.item(2).text() == "word_c"
+ assert wList.listBox.item(3).text() == "word_f"
+ assert wList.listBox.item(4).text() == "word_g"
+
+ # Add a blank word
+ wList.newEntry.setText(" ")
+ assert not wList._doAdd()
+
+ # Add an existing word
+ wList.newEntry.setText("word_c")
+ assert not wList._doAdd()
+
+ # Add a new word
+ wList.newEntry.setText("word_d")
+ assert wList._doAdd()
+
+ # Check that the content now
+ assert wList.listBox.item(0).text() == "word_a"
+ assert wList.listBox.item(1).text() == "word_b"
+ assert wList.listBox.item(2).text() == "word_c"
+ assert wList.listBox.item(3).text() == "word_d"
+ assert wList.listBox.item(4).text() == "word_f"
+ assert wList.listBox.item(5).text() == "word_g"
+
+ # Delete a word
+ wList.newEntry.setText("delete_me")
+ assert wList._doAdd()
+ assert wList.listBox.item(0).text() == "delete_me"
+
+ delItem = wList.listBox.findItems("delete_me", Qt.MatchExactly)[0]
+ assert delItem.text() == "delete_me"
+ delItem.setSelected(True)
+ wList._doDelete()
+ assert wList.listBox.findItems("delete_me", Qt.MatchExactly) == []
+ assert wList.listBox.item(0).text() == "word_a"
+
+ # Save files
+ assert wList._doSave()
+ assert readFile(dictFile) == (
+ "word_a\n"
+ "word_b\n"
+ "word_c\n"
+ "word_d\n"
+ "word_f\n"
+ "word_g\n"
+ )
+
+ # Save again and make it fail
+ monkeypatch.setattr("builtins.open", causeOSError)
+ assert not wList._doSave()
+
+ # qtbot.stopForInteraction()
+ wList._doClose()
+
+# END Test testGuiWordList_Dialog