345 lines
12 KiB
Python
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
|