diff --git a/lib/__init__.py b/lib/__init__.py
index dd554ce..77356d7 100644
--- a/lib/__init__.py
+++ b/lib/__init__.py
@@ -4,4 +4,4 @@ from .books import Book
from .person import PersonDialog
from .read import ReadDialog
from .session import SessionDialog
-from .words import Definition, Word, DefinitionArea
+from .words import Definition, DefinitionArea, Word
diff --git a/lib/read.py b/lib/read.py
index 6678df8..091a12c 100644
--- a/lib/read.py
+++ b/lib/read.py
@@ -6,6 +6,7 @@ from PyQt6.QtCore import QPoint, QResource, Qt, QTimer, pyqtSignal, pyqtSlot
from PyQt6.QtGui import (
QBrush,
QColor,
+ QCursor,
QKeyEvent,
QPainter,
QPainterPath,
@@ -20,6 +21,7 @@ from lib import query_error
from lib.preferences import Preferences
from lib.session import SessionDialog
from lib.sounds import SoundOff
+from lib.words import Word
from ui.ReadDialog import Ui_ReadDialog
@@ -61,10 +63,6 @@ class ReadDialog(QDialog, Ui_ReadDialog):
doc = self.paraEdit.document()
assert doc is not None
doc.setDefaultStyleSheet(styleSheet)
- self.defEdit.setReadOnly(True)
- doc = self.defEdit.document()
- assert doc is not None
- doc.setDefaultStyleSheet(styleSheet)
self.show_section(self.section_id)
self.block = blockNumber
self.savePosition()
@@ -73,7 +71,7 @@ class ReadDialog(QDialog, Ui_ReadDialog):
# Connect widgets
#
self.defineBtn.clicked.connect(self.defineAction)
- self.playBtn.clicked.connect(self.playAction)
+ # self.playBtn.clicked.connect(self.playAction)
self.scrollBtn.clicked.connect(self.scrollAction)
self.nextBtn.clicked.connect(self.nextAction)
self.prevBtn.clicked.connect(self.prevAction)
@@ -90,6 +88,7 @@ class ReadDialog(QDialog, Ui_ReadDialog):
self.newParagraph.connect(self.session.addBlock)
self.playSound.connect(self.sound.playSound)
self.playAlert.connect(self.sound.alert)
+ self.definition.pronounce.connect(self.sound.playSound)
return
#
@@ -109,32 +108,6 @@ class ReadDialog(QDialog, Ui_ReadDialog):
self.newParagraph.emit(self.section_id, self.block)
return
- @pyqtSlot()
- def recursiveDef(self) -> None:
- cursor = self.defEdit.textCursor()
- selection = cursor.selectedText().strip()
- if len(selection) <= 0:
- return
- query = QSqlQuery()
- query.prepare("SELECT * FROM words " "WHERE word = :word")
- query.bindValue(":word", selection)
- if not query.exec():
- query_error(query)
- if not query.next():
- response = requests.get(
- f"https://api.dictionaryapi.dev/api/v2/entries/en/{selection}"
- )
- if response.status_code != 200:
- return
- definitions = json.loads(response.content.decode("utf-8"))
- definition = definitions[0]
- word_id = None
- else:
- definition = query.value("definition")
- word_id = query.value("word_id")
- self.setDefEdit(selection, word_id, definition)
- return
-
@pyqtSlot()
def sessionAction(self) -> None:
self.sessionSignal.emit()
@@ -143,62 +116,34 @@ class ReadDialog(QDialog, Ui_ReadDialog):
@pyqtSlot()
def playAction(self) -> None:
- idx = self.stackedWidget.currentIndex()
- if idx == 0: # Reading
- # find word
- cursor = self.paraEdit.textCursor()
- fmt = cursor.charFormat()
- if not fmt.fontUnderline():
- self.addWord(self.paraEdit)
- cursor = self.paraEdit.textCursor()
- doc = self.paraEdit.document()
- assert doc is not None
- textBlock = doc.findBlock(cursor.position())
- blockNum = textBlock.blockNumber()
- query = QSqlQuery()
- query.prepare(
- "SELECT w.* FROM word_block wb "
- "LEFT JOIN words w "
- "ON (w.word_id = wb.word_id) "
- "WHERE :position BETWEEN wb.start AND wb.end "
- "AND wb.block = :block AND wb.section_id = :section_id"
- )
- query.bindValue(
- ":position", cursor.position() - textBlock.position()
- )
- query.bindValue(":block", blockNum)
- query.bindValue(":section_id", self.section_id)
- if not query.exec():
- query_error(query)
- if not query.next():
- return
- data = json.loads(query.value("definition"))
- if "phonetics" in data:
- self.phonetics = data["phonetics"]
- else:
- self.phonetics = None
- if not self.phonetics:
+ if self.stackedWidget.currentIndex() != 0:
return
- print(self.tr("Searching for audio file"))
- for entry in self.phonetics:
- if len(entry["audio"]) > 0:
- # self.parent().playAlert.emit()
- print(self.tr("playing ") + f"{entry['audio']}")
- self.playSound.emit(entry["audio"])
+
+ # find word
+ cursor = self.paraEdit.textCursor()
+ if cursor.hasSelection():
+ text = cursor.selectedText().strip()
+ else:
+ cursor.select(QTextCursor.SelectionType.WordUnderCursor)
+ text = cursor.selectedText().strip()
+ word = Word(text)
+ word.playPRS()
return
@pyqtSlot()
def scrollAction(self) -> None:
- position = (
- self.paraEdit.document().findBlockByNumber(self.block).position()
- )
+ doc = self.paraEdit.document()
+ assert doc is not None
+ position = doc.findBlockByNumber(self.block).position()
cursor = self.paraEdit.textCursor()
cursor.setPosition(position)
pos = self.paraEdit.mapTo(
self, self.paraEdit.cursorRect(cursor).topLeft()
)
top = self.paraEdit.mapTo(self, QPoint(0, 0))
- value = self.paraEdit.verticalScrollBar().value()
+ sb = self.paraEdit.verticalScrollBar()
+ assert sb is not None
+ value = sb.value()
#
# XXX - Where does "4" come from?
#
@@ -214,8 +159,8 @@ class ReadDialog(QDialog, Ui_ReadDialog):
if msPerTick < 3:
msPerTick = 3
self.target = value + delta
- if self.target > self.paraEdit.verticalScrollBar().maximum():
- self.target = self.paraEdit.verticalScrollBar().maximum() - 1
+ if self.target > sb.maximum():
+ self.target = sb.maximum() - 1
timer = QTimer(self)
timer.timeout.connect(self.softTick)
timer.start(msPerTick)
@@ -223,24 +168,26 @@ class ReadDialog(QDialog, Ui_ReadDialog):
@pyqtSlot()
def softTick(self) -> None:
- value = self.paraEdit.verticalScrollBar().value()
- maxValue = self.paraEdit.verticalScrollBar().maximum()
+ sb = self.paraEdit.verticalScrollBar()
+ assert sb is not None
+ value = sb.value()
+ maxValue = sb.maximum()
sender: QTimer = cast(QTimer, self.sender())
if self.pxPerTick < 0: # moving content up
if value < self.target:
sender.stop()
return
else:
- if value > self.target:
+ if value > self.target or value >= maxValue:
sender.stop()
return
value += self.pxPerTick
- self.paraEdit.verticalScrollBar().setValue(value)
+ sb.setValue(value)
self.update()
return
@pyqtSlot(int)
- def scrollSlot(self, value: int) -> None:
+ def scrollSlot(self, _: int) -> None:
self.update()
return
@@ -265,41 +212,19 @@ class ReadDialog(QDialog, Ui_ReadDialog):
#
@pyqtSlot()
def defineAction(self) -> None:
- editor = self.paraEdit
- if self.stackedWidget.currentIndex() > 0:
- editor = self.defEdit
- self.addWord(editor)
+ if self.stackedWidget.currentIndex() != 0:
+ return
+ cursor = self.paraEdit.textCursor()
+ if cursor.hasSelection():
+ text = cursor.selectedText().strip()
+ else:
+ cursor.select(cursor.SelectionType.WordUnderCursor)
+ text = cursor.selectedText().strip()
+ word = Word(text)
+ self.definition.setWord(word)
self.showDefinition()
return
- def defToHtml(self, word: str, definition: Dict[str, Any]) -> str:
- SPEAKER = "\U0001F508"
- html = f'
{word}
' + "\n"
- try:
- words: List[str] = []
- for phonetic in definition["phonetics"]:
- # XXX - Some phonetics have text and audio but audio is empty,
- # some have just text and some have just audio
- if phonetic["text"] in words:
- continue
- words.append(phonetic["text"])
- html += f'{phonetic["text"]}'
- if "audio" in phonetic:
- html += f'{SPEAKER}'
- html += "
\n"
- except Exception:
- pass
- print(html + "\n")
- html += '' + "\n"
- for meaning in definition["meanings"]:
- html += f"- {meaning['partOfSpeech']}"
- html += '
'
- for a_def in meaning["definitions"]:
- html += f"- {a_def['definition']}
\n"
- html += "
\n"
- html += "
\n\n"
- return html
-
def load_book(self, person_id: int) -> None:
query = QSqlQuery()
query.prepare(
@@ -346,11 +271,13 @@ class ReadDialog(QDialog, Ui_ReadDialog):
self.paraEdit.clear()
cursor = self.paraEdit.textCursor()
cursor.insertHtml(self.sections[sequence])
+ doc = self.paraEdit.document()
+ assert doc is not None
if start:
self.block = 0
else:
- self.block = self.paraEdit.document().blockCount() - 1
- textBlock = self.paraEdit.document().findBlockByNumber(self.block)
+ self.block = doc.blockCount() - 1
+ textBlock = doc.findBlockByNumber(self.block)
cursor.setPosition(textBlock.position())
self.paraEdit.setTextCursor(cursor)
self.paraEdit.ensureCursorVisible()
@@ -378,7 +305,7 @@ class ReadDialog(QDialog, Ui_ReadDialog):
blockNum = query.value("block")
start = query.value("start")
end = query.value("end")
- textBlock = self.paraEdit.document().findBlockByNumber(blockNum)
+ textBlock = doc.findBlockByNumber(blockNum)
cursor.setPosition(
start + textBlock.position(), QTextCursor.MoveMode.MoveAnchor
)
@@ -407,13 +334,13 @@ class ReadDialog(QDialog, Ui_ReadDialog):
super().keyPressEvent(event)
return
- def paintEvent(self, e: QPaintEvent | None) -> None:
+ def paintEvent(self, _: QPaintEvent | None) -> None:
idx = self.stackedWidget.currentIndex()
if idx > 0:
return
- position = (
- self.paraEdit.document().findBlockByNumber(self.block).position()
- )
+ doc = self.paraEdit.document()
+ assert doc is not None
+ position = doc.findBlockByNumber(self.block).position()
cursor = self.paraEdit.textCursor()
cursor.setPosition(position)
#
@@ -435,7 +362,9 @@ class ReadDialog(QDialog, Ui_ReadDialog):
def nextParagraph(self) -> None:
self.block += 1
- if self.block >= self.paraEdit.document().blockCount():
+ doc = self.paraEdit.document()
+ assert doc is not None
+ if self.block >= doc.blockCount():
self.nextSection()
self.savePosition()
self.newParagraph.emit(self.section_id, self.block)
@@ -449,157 +378,6 @@ class ReadDialog(QDialog, Ui_ReadDialog):
self.savePosition()
return
- def addWord(self, editor: QTextEdit) -> None:
- #
- # Find the word
- #
- cursor = editor.textCursor()
- word = cursor.selectedText()
- start = cursor.selectionStart()
- end = cursor.selectionEnd()
- if start != end:
- word = word.strip()
- if len(word) == 0 or word.find(" ") >= 0:
- cursor.select(QTextCursor.SelectionType.WordUnderCursor)
- word = cursor.selectedText()
- word = word.strip()
- start = cursor.selectionStart()
- end = cursor.selectionEnd()
- if start > end:
- tmp = start
- start = end
- end = tmp
- #
- # Find the block
- #
- document = editor.document()
- assert document is not None
- textBlock = document.findBlock(cursor.position())
- blockNum = textBlock.blockNumber()
- start = start - textBlock.position()
- end = end - textBlock.position()
-
- query = QSqlQuery()
- query.prepare("SELECT * FROM words WHERE word = :word")
- query.bindValue(":word", word)
- if not query.exec():
- query_error(query)
- if query.next(): # we have something
- self.defined(query.value("word_id"), blockNum, start, end)
- return
- #
- # Get the defintion
- #
- response = requests.get(
- f"https://api.dictionaryapi.dev/api/v2/entries/en/{word}"
- )
- if response.status_code != 200:
- print(f"{word}: {response.content.decode('utf8')}")
- self.playAlert.emit()
- return
- definitions = json.loads(response.content.decode("utf-8"))
- definition = definitions[0]
- query.prepare(
- "INSERT INTO words (word, definition) "
- "VALUES (:word, :definition)"
- )
- query.bindValue(":word", word)
- query.bindValue(":definition", json.dumps(definition))
- if not query.exec():
- query_error(query)
- self.defined(query.lastInsertId(), blockNum, start, end)
- return
-
- def defined(
- self, word_id: int, blockNum: int, start: int, end: int
- ) -> None:
- query = QSqlQuery()
- query.prepare(
- "SELECT * FROM word_block "
- "WHERE section_id = :section_id "
- "AND block = :block "
- "AND start = :start "
- "AND end = :end"
- )
- query.bindValue(":word_id", word_id)
- query.bindValue(":section_id", self.section_id)
- query.bindValue(":block", blockNum)
- query.bindValue(":start", start)
- query.bindValue(":end", end)
- if not query.exec():
- query_error(query)
- if not query.next():
- query.prepare(
- "INSERT INTO word_block VALUES "
- "( :word_id, :section_id, :block, :start, :end)"
- )
- query.bindValue(":word_id", word_id)
- query.bindValue(":section_id", self.section_id)
- query.bindValue(":block", blockNum)
- query.bindValue(":start", start)
- query.bindValue(":end", end)
- if not query.exec():
- query_error(query)
-
- def_format = QTextCharFormat()
- def_format.setFontUnderline(True)
- cursor = QTextCursor(self.paraEdit.document())
- textBlock = self.paraEdit.document().findBlockByNumber(blockNum)
- cursor.setPosition(
- start + textBlock.position(), QTextCursor.MoveMode.MoveAnchor
- )
- cursor.setPosition(
- end + textBlock.position(), QTextCursor.MoveMode.KeepAnchor
- )
- cursor.mergeCharFormat(def_format)
- return
-
- # XXX - rename
- #
- # Create a definition for the word under the cursor on the current
- # panel.
- #
- def display_definition(self, idx: int) -> bool:
- if idx == 0:
- editor = self.paraEdit
- else:
- editor = self.defEdit
- cursor = editor.textCursor()
- cursor.select(QTextCursor.SelectionType.WordUnderCursor)
- word = cursor.selectedText()
- # fmt = cursor.charFormat()
- # if not fmt.fontUnderline():
- # self.addWord(editor)
- query = QSqlQuery()
- query.prepare("SELECT w.* FROM words w " "WHERE word = :word")
- query.bindValue(":word", word)
- if not query.exec():
- query_error(query)
- if not query.next():
- return False
- word = query.value("word")
- definition = json.loads(query.value("definition"))
- self.setDefEdit(word, query.value("word_id"), definition)
- return True
-
- def setDefEdit(
- self, word: str, word_id: int, definition: Dict[str, str]
- ) -> None:
- if "phonetics" in definition:
- self.phonetics = definition["phonetics"]
- else:
- self.phonetics = None
- doc = self.defEdit.document()
- assert doc is not None
- doc.clear()
- cursor = self.defEdit.textCursor()
- cursor.insertHtml(self.defToHtml(word, definition))
- cursor.setPosition(0)
- self.defEdit.setTextCursor(cursor)
- if word_id:
- self.displayedWord.emit(word_id)
- return
-
@pyqtSlot()
def returnAction(self) -> None:
self.returnBtn.setVisible(False)
@@ -614,12 +392,9 @@ class ReadDialog(QDialog, Ui_ReadDialog):
return
def showDefinition(self) -> None:
- idx = self.stackedWidget.currentIndex()
self.returnBtn.setVisible(True)
self.nextBtn.setText(self.tr("Next Definition"))
self.prevBtn.setText(self.tr("Previous Definition"))
- if not self.display_definition(idx):
- return
self.stackedWidget.setCurrentIndex(1)
self.update()
return
@@ -631,9 +406,9 @@ class ReadDialog(QDialog, Ui_ReadDialog):
def savePosition(self) -> None:
cursor = self.paraEdit.textCursor()
- cursor.setPosition(
- self.paraEdit.document().findBlockByNumber(self.block).position()
- )
+ doc = self.paraEdit.document()
+ assert doc is not None
+ cursor.setPosition(doc.findBlockByNumber(self.block).position())
self.paraEdit.setTextCursor(cursor)
self.scrollTo(cursor.position())
self.paraEdit.ensureCursorVisible()