Files
esl-reader/lib/read.py
Christopher T. Johnson 0b02ed2201 Mostly working Reader
2023-11-14 10:49:47 -05:00

345 lines
12 KiB
Python

import json
from PyDictionary import PyDictionary # type: ignore[import-untyped]
from PyQt6.QtCore import QRect, Qt, pyqtSlot
from PyQt6.QtGui import (
QBrush,
QColor,
QFont,
QPainter,
QPainterPath,
QTextCharFormat,
QTextCursor,
QTextDocument,
QTextListFormat,
)
from PyQt6.QtSql import QSqlDatabase, QSqlQuery, QSqlQueryModel
from PyQt6.QtWidgets import QDialog, QPushButton
from main import query_error
from ui.EditDialog import Ui_Dialog
class EditDialog(QDialog, Ui_Dialog):
def __init__(self, person_id: int) -> None:
super(EditDialog, self).__init__()
self.person_id = person_id
self.setupUi(self)
self.load_book(self.person_id)
blockNumber = self.block
self.paraEdit.setReadOnly(True)
self.defEdit.setReadOnly(True)
self.show_section(self.section_id)
self.block = blockNumber
self.savePosition()
self.stackedWidget.setCurrentIndex(0)
self.nextParaBtn = QPushButton(parent=self.widget)
self.nextParaBtn.setObjectName("nextParaBtn")
self.verticalLayout.addWidget(self.nextParaBtn)
self.nextParaBtn.setText("Next Paragraph")
self.defineBtn.clicked.connect(self.defineAction)
self.showBtn.clicked.connect(self.showAction)
self.nextBtn.clicked.connect(self.nextAction)
self.prevBtn.clicked.connect(self.prevAction)
self.nextParaBtn.clicked.connect(self.nextParaAction)
self.paraEdit.verticalScrollBar().valueChanged.connect(self.scrollSlot)
return
@pyqtSlot(int)
def scrollSlot(self, value):
self.update()
return
def load_book(self, person_id: int) -> None:
query = QSqlQuery()
query.prepare(
"SELECT pb.* FROM people p "
"LEFT JOIN person_book pb "
"ON (p.book_id = pb.book_id "
"AND p.person_id = pb.person_id) "
"WHERE p.person_id = :person_id"
)
query.bindValue(":person_id", person_id)
if not query.exec():
query_error(query)
if not query.next():
self.done(0)
self.book_id = query.value("book_id")
self.section_id = query.value("section_id")
self.block = query.value("block")
self.sections = []
self.section_map = {}
self.sequence_map = {}
query.prepare(
"SELECT * FROM sections " "WHERE book_id = :book_id " "ORDER BY sequence"
)
query.bindValue(":book_id", self.book_id)
if not query.exec():
query_error(query)
while query.next():
self.sections.append(query.value("content"))
self.section_map[query.value("section_id")] = query.value("sequence")
self.sequence_map[query.value("sequence")] = query.value("section_id")
return
def show_section(self, section_id, start=True):
sequence = self.section_map[section_id]
self.setWindowTitle(f"Section {sequence}")
self.paraEdit.clear()
cursor = self.paraEdit.textCursor()
cursor.insertHtml(self.sections[sequence])
if start:
self.block = 0
else:
self.block = self.paraEdit.document().blockCount() - 1
textBlock = self.paraEdit.document().findBlockByNumber(self.block)
cursor.setPosition(textBlock.position())
self.paraEdit.setTextCursor(cursor)
self.paraEdit.ensureCursorVisible()
return
def mousePressEvent(self, event):
return
def paintEvent(self, e):
position = self.paraEdit.document().findBlockByNumber(self.block).position()
cursor = self.paraEdit.textCursor()
cursor.setPosition(position)
rect = self.paraEdit.cursorRect(cursor)
# print(rect)
pos = self.paraEdit.mapToParent(self.paraEdit.pos())
painter = QPainter(self)
brush = QBrush()
brush.setColor(QColor("green"))
brush.setStyle(Qt.BrushStyle.SolidPattern)
path = QPainterPath()
path.moveTo(0, 0)
path.lineTo(pos.x() - 1.0, pos.y() / 2.0)
path.lineTo(0, pos.y())
path.lineTo(0, 0)
# XXX - Replace the guess with a calculated value
painter.translate(1.0, pos.y() + rect.y() + 12)
painter.fillPath(path, brush)
@pyqtSlot()
def nextParaAction(self) -> None:
self.block += 1
if self.block >= self.paraEdit.document().blockCount():
self.nextAction()
return
self.savePosition()
return
@pyqtSlot()
def defineAction(self) -> None:
cursor = self.paraEdit.textCursor()
word = cursor.selectedText()
start = cursor.selectionStart()
end = cursor.selectionEnd()
if start != end:
word = word.strip()
if len(word) == 0 or word.find(" ") >= 0:
return
if start > end:
tmp = start
start = end
end = tmp
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"), start, end)
return
dictionary = PyDictionary()
meaning = json.dumps(dictionary.meaning(word))
por = dictionary.translate(word, "pt")
query.prepare(
"INSERT INTO words (word, definition, translation) "
"VALUES (:word, :definition, :translation)"
)
query.bindValue(":word", word)
query.bindValue(":definition", meaning)
query.bindValue(":translation", por)
if not query.exec():
query_error(query)
self.defined(query.lastInsertId(), start, end)
return
def defined(self, word_id: int, start: int, end: int) -> None:
query = QSqlQuery()
query.prepare(
"SELECT * FROM word_paragraph "
"WHERE word_id = :word_id "
"AND paragraph_id = :paragraph_id "
"AND start = :start "
"AND end = :end"
)
query.bindValue(":word_id", word_id)
query.bindValue(":paragraph_id", self.paragraph_id)
query.bindValue(":start", start)
query.bindValue(":end", end)
if not query.exec():
query_error(query)
if query.next():
return
query.prepare(
"INSERT INTO word_paragraph VALUES "
"( :word_id, :paragraph_id, :start, :end)"
)
query.bindValue(":word_id", word_id)
query.bindValue(":paragraph_id", self.paragraph_id)
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())
cursor.setPosition(start, QTextCursor.MoveMode.MoveAnchor)
cursor.setPosition(end, QTextCursor.MoveMode.KeepAnchor)
cursor.setCharFormat(def_format)
return
def display_definition(self) -> None:
cursor = self.paraEdit.textCursor()
query = QSqlQuery()
query.prepare(
"SELECT w.* FROM word_paragraph wp "
"LEFT JOIN words w "
"ON (w.word_id = wp.word_id) "
"WHERE :position BETWEEN wp.start AND wp.end "
"AND wp.paragraph_id = :paragraph_id"
)
query.bindValue(":position", cursor.position())
query.bindValue(":paragraph_id", self.paragraph_id)
print("display_definition()", cursor.position())
if not query.exec():
query_error(query)
if not query.next():
return
word = query.value("word")
definition = json.loads(query.value("definition"))
self.defEdit.document().clear()
cursor = self.defEdit.textCursor()
word_format = QTextCharFormat()
word_format.setFontPointSize(48)
word_format.setFontWeight(QFont.Weight.Bold)
cursor.insertText(word, word_format)
typeFormat = QTextListFormat()
typeFormat.setStyle(QTextListFormat.Style.ListDisc)
typeFormat.setIndent(1)
defFormat = QTextListFormat()
defFormat.setStyle(QTextListFormat.Style.ListCircle)
defFormat.setIndent(2)
word_format.setFontWeight(QFont.Weight.Normal)
word_format.setFontPointSize(16)
cursor.setCharFormat(word_format)
for key in definition.keys():
cursor.insertList(typeFormat)
cursor.insertText(key)
cursor.insertList(defFormat)
first = True
for a_def in definition[key]:
if not first:
cursor.insertBlock()
cursor.insertText(a_def)
first = False
return
@pyqtSlot()
def showAction(self) -> None:
idx = self.stackedWidget.currentIndex()
if idx == 0:
self.display_definition()
self.stackedWidget.setCurrentIndex(1 - idx)
return
def nextDefinition(self):
cursor = self.paraEdit.textCursor()
position = cursor.position()
print(position)
formats = cursor.block().textFormats()
found = None
for f in formats:
wc = QTextCursor(cursor)
wc.setPosition(f.start)
wc.movePosition(
QTextCursor.MoveOperation.Right,
QTextCursor.MoveMode.KeepAnchor,
f.length,
)
word = wc.selectedText()
cf = wc.charFormat()
if f.start <= position:
continue
if not cf.fontUnderline():
continue
if not found:
found = f
elif f.start < found.start:
found = f
if found:
cursor.setPosition(found.start)
self.paraEdit.setTextCursor(cursor)
self.display_definition()
return
def scrollTo(self, position):
cursor = self.paraEdit.textCursor()
cursor.setPosition(position)
rect = self.paraEdit.cursorRect(cursor)
print(rect)
return
def savePosition(self) -> None:
cursor = self.paraEdit.textCursor()
cursor.setPosition(
self.paraEdit.document().findBlockByNumber(self.block).position()
)
self.paraEdit.setTextCursor(cursor)
self.scrollTo(cursor.position())
self.paraEdit.ensureCursorVisible()
self.update()
query = QSqlQuery()
query.prepare(
"UPDATE person_book SET section_id = :section_id, "
"block = :block "
"WHERE book_id = :book_id "
"AND person_id = :person_id"
)
query.bindValue(":section_id", self.section_id)
query.bindValue(":block", self.block)
query.bindValue(":book_id", self.book_id)
query.bindValue(":person_id", self.person_id)
if not query.exec():
query_error(query)
return
@pyqtSlot()
def nextAction(self) -> None:
if self.stackedWidget.currentIndex() == 1:
self.nextDefinition()
return
sequence = self.section_map[self.section_id]
sequence += 1
self.section_id = self.sequence_map[sequence]
self.show_section(self.section_id)
self.savePosition()
return
@pyqtSlot()
def prevAction(self) -> None:
if self.stackedWidget.currentIndex() == 1:
return
sequence = self.section_map[self.section_id]
if sequence < 1:
return
sequence -= 1
self.section_id = self.sequence_map[sequence]
self.show_section(self.section_id, False)
self.savePosition()
return