Move to chapter based reader
This commit is contained in:
38
lib/books.py
38
lib/books.py
@@ -11,7 +11,6 @@ from main import query_error
|
|||||||
class Book:
|
class Book:
|
||||||
sections: List[str] = []
|
sections: List[str] = []
|
||||||
metadata: Dict[str, str] = {}
|
metadata: Dict[str, str] = {}
|
||||||
words = {}
|
|
||||||
|
|
||||||
def __init__(self, src: str) -> None:
|
def __init__(self, src: str) -> None:
|
||||||
super(Book, self).__init__()
|
super(Book, self).__init__()
|
||||||
@@ -131,61 +130,66 @@ class Book:
|
|||||||
def parse_section(self, src: str, href: str) -> None:
|
def parse_section(self, src: str, href: str) -> None:
|
||||||
newdom = xml.dom.getDOMImplementation().createDocument("", "html", None)
|
newdom = xml.dom.getDOMImplementation().createDocument("", "html", None)
|
||||||
|
|
||||||
def strip_node(elm: xml.dom.minidom.Element) -> xml.dom.minidom.Node:
|
def strip_node(elm: xml.dom.minidom.Element) -> xml.dom.minidom.Element:
|
||||||
if elm.nodeType == xml.dom.Node.TEXT_NODE:
|
if elm.nodeType == xml.dom.Node.TEXT_NODE:
|
||||||
return cast(
|
return cast(
|
||||||
xml.dom.minidom.Node,
|
xml.dom.minidom.Element,
|
||||||
newdom.createTextNode(cast(xml.dom.minidom.Text, elm).data),
|
newdom.createTextNode(cast(xml.dom.minidom.Text, elm).data),
|
||||||
)
|
)
|
||||||
|
|
||||||
newelm = newdom.createElement(elm.localName)
|
newelm: xml.dom.minidom.Element = newdom.createElement(elm.localName)
|
||||||
node = elm.firstChild
|
node = elm.firstChild
|
||||||
while node:
|
while node:
|
||||||
if node.nodeType == xml.dom.Node.TEXT_NODE:
|
elm = cast(xml.dom.minidom.Element, node)
|
||||||
text = node.data
|
if elm.nodeType == xml.dom.Node.TEXT_NODE:
|
||||||
|
text = cast(xml.dom.minidom.Text, elm).data
|
||||||
if text:
|
if text:
|
||||||
text = text.strip()
|
text = text.strip()
|
||||||
if text and len(text) > 0:
|
if text and len(text) > 0:
|
||||||
newelm.appendChild(newdom.createTextNode(text))
|
newelm.appendChild(newdom.createTextNode(text))
|
||||||
elif node.localName == "img":
|
elif elm.localName == "img":
|
||||||
pass
|
pass
|
||||||
elif node.localName == "a":
|
elif elm.localName == "a":
|
||||||
a_node = node.firstChild
|
a_node = cast(xml.dom.minidom.Element, elm.firstChild)
|
||||||
while a_node:
|
while a_node:
|
||||||
if a_node.nodeType == xml.dom.Node.TEXT_NODE:
|
if a_node.nodeType == xml.dom.Node.TEXT_NODE:
|
||||||
newelm.appendChild(newdom.createTextNode(a_node.data))
|
text = cast(xml.dom.minidom.Text, a_node)
|
||||||
|
newelm.appendChild(newdom.createTextNode(text.data))
|
||||||
else:
|
else:
|
||||||
newelm.appendChild(strip_node(a_node))
|
newelm.appendChild(strip_node(a_node))
|
||||||
a_node = a_node.nextSibling
|
a_node = a_node.nextSibling
|
||||||
else:
|
else:
|
||||||
newelm.appendChild(strip_node(node))
|
newelm.appendChild(strip_node(elm))
|
||||||
node = node.nextSibling
|
node = node.nextSibling
|
||||||
return newelm
|
return newelm
|
||||||
|
|
||||||
def parse_node(parent: xml.dom.Node, elm: xml.dom.Node) -> None:
|
def parse_node(
|
||||||
|
parent: xml.dom.minidom.Element, elm: xml.dom.minidom.Element
|
||||||
|
) -> None:
|
||||||
if elm.nodeType == xml.dom.Node.ELEMENT_NODE:
|
if elm.nodeType == xml.dom.Node.ELEMENT_NODE:
|
||||||
if elm.localName.startswith("h"):
|
tag: str = cast(str, elm.localName)
|
||||||
|
if tag.startswith("h"):
|
||||||
clone = strip_node(elm)
|
clone = strip_node(elm)
|
||||||
parent.appendChild(clone)
|
parent.appendChild(clone)
|
||||||
elif elm.localName == "p":
|
elif tag == "p":
|
||||||
clone = strip_node(elm)
|
clone = strip_node(elm)
|
||||||
clone.normalize()
|
clone.normalize()
|
||||||
parent.appendChild(clone)
|
parent.appendChild(clone)
|
||||||
else:
|
else:
|
||||||
node = elm.firstChild
|
node = elm.firstChild
|
||||||
while node:
|
while node:
|
||||||
parse_node(parent, node)
|
parse_node(parent, cast(xml.dom.minidom.Element, node))
|
||||||
node = node.nextSibling
|
node = node.nextSibling
|
||||||
return
|
return
|
||||||
|
|
||||||
with open(f"{src}/{href}") as f:
|
with open(f"{src}/{href}") as f:
|
||||||
dom = xml.dom.minidom.parse(f)
|
dom = xml.dom.minidom.parse(f)
|
||||||
title = dom.getElementsByTagName("title")[0].firstChild.data
|
# title = dom.getElementsByTagName("title")[0].firstChild.data
|
||||||
body = dom.getElementsByTagName("body")[0]
|
body = dom.getElementsByTagName("body")[0]
|
||||||
section = newdom.createElement("body")
|
section = newdom.createElement("body")
|
||||||
node = body.firstChild
|
node = body.firstChild
|
||||||
while node:
|
while node:
|
||||||
parse_node(section, node)
|
parse_node(section, cast(xml.dom.minidom.Element, node))
|
||||||
node = node.nextSibling
|
node = node.nextSibling
|
||||||
self.sections.append(section.toxml())
|
self.sections.append(section.toxml())
|
||||||
return
|
return
|
||||||
|
|||||||
263
lib/read.py
263
lib/read.py
@@ -1,13 +1,16 @@
|
|||||||
import json
|
import json
|
||||||
|
from typing import cast
|
||||||
|
|
||||||
from PyDictionary import PyDictionary # type: ignore[import-untyped]
|
import requests
|
||||||
from PyQt6.QtCore import QRect, Qt, pyqtSlot
|
from PyQt6.QtCore import QPoint, QRect, Qt, QTimer, pyqtSlot
|
||||||
from PyQt6.QtGui import (
|
from PyQt6.QtGui import (
|
||||||
QBrush,
|
QBrush,
|
||||||
QColor,
|
QColor,
|
||||||
QFont,
|
QFont,
|
||||||
|
QMouseEvent,
|
||||||
QPainter,
|
QPainter,
|
||||||
QPainterPath,
|
QPainterPath,
|
||||||
|
QPaintEvent,
|
||||||
QTextCharFormat,
|
QTextCharFormat,
|
||||||
QTextCursor,
|
QTextCursor,
|
||||||
QTextDocument,
|
QTextDocument,
|
||||||
@@ -21,10 +24,26 @@ from ui.EditDialog import Ui_Dialog
|
|||||||
|
|
||||||
|
|
||||||
class EditDialog(QDialog, Ui_Dialog):
|
class EditDialog(QDialog, Ui_Dialog):
|
||||||
|
block: int
|
||||||
|
|
||||||
def __init__(self, person_id: int) -> None:
|
def __init__(self, person_id: int) -> None:
|
||||||
super(EditDialog, self).__init__()
|
super(EditDialog, self).__init__()
|
||||||
self.person_id = person_id
|
self.person_id = person_id
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
|
#
|
||||||
|
# Override UI
|
||||||
|
#
|
||||||
|
font = QFont()
|
||||||
|
font.setFamily("OpenDyslexic")
|
||||||
|
font.setPointSize(14)
|
||||||
|
self.paraEdit.setFont(font)
|
||||||
|
self.nextParaBtn = QPushButton(parent=self.widget)
|
||||||
|
self.nextParaBtn.setObjectName("nextParaBtn")
|
||||||
|
self.verticalLayout.addWidget(self.nextParaBtn)
|
||||||
|
self.nextParaBtn.setText("Next Paragraph")
|
||||||
|
#
|
||||||
|
# End overrides
|
||||||
|
#
|
||||||
self.load_book(self.person_id)
|
self.load_book(self.person_id)
|
||||||
blockNumber = self.block
|
blockNumber = self.block
|
||||||
self.paraEdit.setReadOnly(True)
|
self.paraEdit.setReadOnly(True)
|
||||||
@@ -33,20 +52,60 @@ class EditDialog(QDialog, Ui_Dialog):
|
|||||||
self.block = blockNumber
|
self.block = blockNumber
|
||||||
self.savePosition()
|
self.savePosition()
|
||||||
self.stackedWidget.setCurrentIndex(0)
|
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.defineBtn.clicked.connect(self.defineAction)
|
||||||
self.showBtn.clicked.connect(self.showAction)
|
self.showBtn.clicked.connect(self.showAction)
|
||||||
self.nextBtn.clicked.connect(self.nextAction)
|
self.nextBtn.clicked.connect(self.nextAction)
|
||||||
self.prevBtn.clicked.connect(self.prevAction)
|
self.prevBtn.clicked.connect(self.prevAction)
|
||||||
self.nextParaBtn.clicked.connect(self.nextParaAction)
|
self.nextParaBtn.clicked.connect(self.nextParaAction)
|
||||||
|
self.wordsBtn.clicked.connect(self.wordAction)
|
||||||
self.paraEdit.verticalScrollBar().valueChanged.connect(self.scrollSlot)
|
self.paraEdit.verticalScrollBar().valueChanged.connect(self.scrollSlot)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def wordAction(self) -> None:
|
||||||
|
pos = self.paraEdit.mapTo(self, self.paraEdit.cursorRect().topLeft())
|
||||||
|
top = self.paraEdit.mapTo(self, QPoint(0, 0))
|
||||||
|
value = self.paraEdit.verticalScrollBar().value()
|
||||||
|
#
|
||||||
|
# XXX - Where does "4" come from?
|
||||||
|
#
|
||||||
|
delta = pos.y() - top.y() - 4
|
||||||
|
self.pxPerTick = int(1000 / delta)
|
||||||
|
if self.pxPerTick < 1:
|
||||||
|
if delta < 0:
|
||||||
|
self.pxPerTick = -1
|
||||||
|
else:
|
||||||
|
self.pxPerTick = 1
|
||||||
|
steps = abs(delta / self.pxPerTick)
|
||||||
|
msPerTick = int(1000 / steps)
|
||||||
|
if msPerTick < 3:
|
||||||
|
msPerTick = 3
|
||||||
|
self.target = value + delta
|
||||||
|
print(f"delta: {delta}, pixels: {self.pxPerTick}, ms: {msPerTick}")
|
||||||
|
timer = QTimer(self)
|
||||||
|
timer.timeout.connect(self.softTick)
|
||||||
|
timer.start(msPerTick)
|
||||||
|
return
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def softTick(self) -> None:
|
||||||
|
value = self.paraEdit.verticalScrollBar().value()
|
||||||
|
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:
|
||||||
|
sender.stop()
|
||||||
|
return
|
||||||
|
value += self.pxPerTick
|
||||||
|
self.paraEdit.verticalScrollBar().setValue(value)
|
||||||
|
self.update()
|
||||||
|
return
|
||||||
|
|
||||||
@pyqtSlot(int)
|
@pyqtSlot(int)
|
||||||
def scrollSlot(self, value):
|
def scrollSlot(self, value: int) -> None:
|
||||||
self.update()
|
self.update()
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -82,7 +141,7 @@ class EditDialog(QDialog, Ui_Dialog):
|
|||||||
self.sequence_map[query.value("sequence")] = query.value("section_id")
|
self.sequence_map[query.value("sequence")] = query.value("section_id")
|
||||||
return
|
return
|
||||||
|
|
||||||
def show_section(self, section_id, start=True):
|
def show_section(self, section_id: int, start: bool = True) -> None:
|
||||||
sequence = self.section_map[section_id]
|
sequence = self.section_map[section_id]
|
||||||
self.setWindowTitle(f"Section {sequence}")
|
self.setWindowTitle(f"Section {sequence}")
|
||||||
self.paraEdit.clear()
|
self.paraEdit.clear()
|
||||||
@@ -96,29 +155,57 @@ class EditDialog(QDialog, Ui_Dialog):
|
|||||||
cursor.setPosition(textBlock.position())
|
cursor.setPosition(textBlock.position())
|
||||||
self.paraEdit.setTextCursor(cursor)
|
self.paraEdit.setTextCursor(cursor)
|
||||||
self.paraEdit.ensureCursorVisible()
|
self.paraEdit.ensureCursorVisible()
|
||||||
|
#
|
||||||
|
# Mark all the defined words with underlines
|
||||||
|
#
|
||||||
|
def_format = QTextCharFormat()
|
||||||
|
def_format.setFontUnderline(True)
|
||||||
|
cursor = QTextCursor(self.paraEdit.document())
|
||||||
|
query = QSqlQuery()
|
||||||
|
query.prepare("SELECT * FROM word_block " "WHERE section_id = :section_id")
|
||||||
|
query.bindValue(":section_id", section_id)
|
||||||
|
if not query.exec():
|
||||||
|
query_error(query)
|
||||||
|
while query.next():
|
||||||
|
#
|
||||||
|
# Define these variables so that the code matches
|
||||||
|
# the defining action
|
||||||
|
#
|
||||||
|
blockNum = query.value("block")
|
||||||
|
start = query.value("start")
|
||||||
|
end = query.value("end")
|
||||||
|
textBlock = self.paraEdit.document().findBlockByNumber(blockNum)
|
||||||
|
cursor.setPosition(
|
||||||
|
start + textBlock.position(), QTextCursor.MoveMode.MoveAnchor
|
||||||
|
)
|
||||||
|
cursor.setPosition(
|
||||||
|
end + textBlock.position(), QTextCursor.MoveMode.KeepAnchor
|
||||||
|
)
|
||||||
|
cursor.setCharFormat(def_format)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def mousePressEvent(self, event):
|
def mousePressEvent(self, event: QMouseEvent | None) -> None:
|
||||||
return
|
return
|
||||||
|
|
||||||
def paintEvent(self, e):
|
def paintEvent(self, e: QPaintEvent | None) -> None:
|
||||||
position = self.paraEdit.document().findBlockByNumber(self.block).position()
|
position = self.paraEdit.document().findBlockByNumber(self.block).position()
|
||||||
cursor = self.paraEdit.textCursor()
|
cursor = self.paraEdit.textCursor()
|
||||||
cursor.setPosition(position)
|
cursor.setPosition(position)
|
||||||
|
#
|
||||||
|
# rect is position in viewport coordenates.
|
||||||
rect = self.paraEdit.cursorRect(cursor)
|
rect = self.paraEdit.cursorRect(cursor)
|
||||||
# print(rect)
|
c_pt = self.paraEdit.mapTo(self, rect.bottomLeft())
|
||||||
pos = self.paraEdit.mapToParent(self.paraEdit.pos())
|
|
||||||
painter = QPainter(self)
|
painter = QPainter(self)
|
||||||
brush = QBrush()
|
brush = QBrush()
|
||||||
brush.setColor(QColor("green"))
|
brush.setColor(QColor("green"))
|
||||||
brush.setStyle(Qt.BrushStyle.SolidPattern)
|
brush.setStyle(Qt.BrushStyle.SolidPattern)
|
||||||
path = QPainterPath()
|
path = QPainterPath()
|
||||||
path.moveTo(0, 0)
|
path.moveTo(0, 0)
|
||||||
path.lineTo(pos.x() - 1.0, pos.y() / 2.0)
|
path.lineTo(18, -rect.height() / 2.0)
|
||||||
path.lineTo(0, pos.y())
|
path.lineTo(0, -rect.height())
|
||||||
path.lineTo(0, 0)
|
path.lineTo(0, 0)
|
||||||
# XXX - Replace the guess with a calculated value
|
painter.translate(1.0, c_pt.y())
|
||||||
painter.translate(1.0, pos.y() + rect.y() + 12)
|
|
||||||
painter.fillPath(path, brush)
|
painter.fillPath(path, brush)
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
@@ -132,6 +219,9 @@ class EditDialog(QDialog, Ui_Dialog):
|
|||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def defineAction(self) -> None:
|
def defineAction(self) -> None:
|
||||||
|
#
|
||||||
|
# Find the word
|
||||||
|
#
|
||||||
cursor = self.paraEdit.textCursor()
|
cursor = self.paraEdit.textCursor()
|
||||||
word = cursor.selectedText()
|
word = cursor.selectedText()
|
||||||
start = cursor.selectionStart()
|
start = cursor.selectionStart()
|
||||||
@@ -144,41 +234,54 @@ class EditDialog(QDialog, Ui_Dialog):
|
|||||||
tmp = start
|
tmp = start
|
||||||
start = end
|
start = end
|
||||||
end = tmp
|
end = tmp
|
||||||
|
#
|
||||||
|
# Find the block
|
||||||
|
#
|
||||||
|
textBlock = self.paraEdit.document().findBlock(cursor.position())
|
||||||
|
blockNum = textBlock.blockNumber()
|
||||||
query = QSqlQuery()
|
query = QSqlQuery()
|
||||||
query.prepare("SELECT * FROM words WHERE word = :word")
|
query.prepare("SELECT * FROM words WHERE word = :word")
|
||||||
query.bindValue(":word", word)
|
query.bindValue(":word", word)
|
||||||
if not query.exec():
|
if not query.exec():
|
||||||
query_error(query)
|
query_error(query)
|
||||||
if query.next(): # we have something
|
if query.next(): # we have something
|
||||||
self.defined(query.value("word_id"), start, end)
|
self.defined(query.value("word_id"), blockNum, start, end)
|
||||||
return
|
return
|
||||||
dictionary = PyDictionary()
|
#
|
||||||
meaning = json.dumps(dictionary.meaning(word))
|
# Get the defintion
|
||||||
por = dictionary.translate(word, "pt")
|
#
|
||||||
|
response = requests.get(
|
||||||
|
f"https://api.dictionaryapi.dev/api/v2/entries/en/{word}"
|
||||||
|
)
|
||||||
|
if response.status_code != 200:
|
||||||
|
print(response.content)
|
||||||
|
return
|
||||||
|
definitions = json.loads(response.content.decode("utf-8"))
|
||||||
|
definition = definitions[0]
|
||||||
query.prepare(
|
query.prepare(
|
||||||
"INSERT INTO words (word, definition, translation) "
|
"INSERT INTO words (word, definition) " "VALUES (:word, :definition)"
|
||||||
"VALUES (:word, :definition, :translation)"
|
|
||||||
)
|
)
|
||||||
query.bindValue(":word", word)
|
query.bindValue(":word", word)
|
||||||
query.bindValue(":definition", meaning)
|
query.bindValue(":definition", json.dumps(definition))
|
||||||
query.bindValue(":translation", por)
|
|
||||||
if not query.exec():
|
if not query.exec():
|
||||||
query_error(query)
|
query_error(query)
|
||||||
self.defined(query.lastInsertId(), start, end)
|
start = start - textBlock.position()
|
||||||
|
end = end - textBlock.position()
|
||||||
|
self.defined(query.lastInsertId(), blockNum, start, end)
|
||||||
return
|
return
|
||||||
|
|
||||||
def defined(self, word_id: int, start: int, end: int) -> None:
|
def defined(self, word_id: int, blockNum: int, start: int, end: int) -> None:
|
||||||
query = QSqlQuery()
|
query = QSqlQuery()
|
||||||
query.prepare(
|
query.prepare(
|
||||||
"SELECT * FROM word_paragraph "
|
"SELECT * FROM word_block "
|
||||||
"WHERE word_id = :word_id "
|
"WHERE section_id = :section_id "
|
||||||
"AND paragraph_id = :paragraph_id "
|
"AND block = :block "
|
||||||
"AND start = :start "
|
"AND start = :start "
|
||||||
"AND end = :end"
|
"AND end = :end"
|
||||||
)
|
)
|
||||||
query.bindValue(":word_id", word_id)
|
query.bindValue(":word_id", word_id)
|
||||||
query.bindValue(":paragraph_id", self.paragraph_id)
|
query.bindValue(":section_id", self.section_id)
|
||||||
|
query.bindValue(":block", blockNum)
|
||||||
query.bindValue(":start", start)
|
query.bindValue(":start", start)
|
||||||
query.bindValue(":end", end)
|
query.bindValue(":end", end)
|
||||||
if not query.exec():
|
if not query.exec():
|
||||||
@@ -186,11 +289,12 @@ class EditDialog(QDialog, Ui_Dialog):
|
|||||||
if query.next():
|
if query.next():
|
||||||
return
|
return
|
||||||
query.prepare(
|
query.prepare(
|
||||||
"INSERT INTO word_paragraph VALUES "
|
"INSERT INTO word_block VALUES "
|
||||||
"( :word_id, :paragraph_id, :start, :end)"
|
"( :word_id, :section_id, :block, :start, :end)"
|
||||||
)
|
)
|
||||||
query.bindValue(":word_id", word_id)
|
query.bindValue(":word_id", word_id)
|
||||||
query.bindValue(":paragraph_id", self.paragraph_id)
|
query.bindValue(":section_id", self.section_id)
|
||||||
|
query.bindValue(":block", blockNum)
|
||||||
query.bindValue(":start", start)
|
query.bindValue(":start", start)
|
||||||
query.bindValue(":end", end)
|
query.bindValue(":end", end)
|
||||||
if not query.exec():
|
if not query.exec():
|
||||||
@@ -198,24 +302,29 @@ class EditDialog(QDialog, Ui_Dialog):
|
|||||||
def_format = QTextCharFormat()
|
def_format = QTextCharFormat()
|
||||||
def_format.setFontUnderline(True)
|
def_format.setFontUnderline(True)
|
||||||
cursor = QTextCursor(self.paraEdit.document())
|
cursor = QTextCursor(self.paraEdit.document())
|
||||||
cursor.setPosition(start, QTextCursor.MoveMode.MoveAnchor)
|
textBlock = self.paraEdit.document().findBlockByNumber(blockNum)
|
||||||
cursor.setPosition(end, QTextCursor.MoveMode.KeepAnchor)
|
cursor.setPosition(
|
||||||
|
start + textBlock.position(), QTextCursor.MoveMode.MoveAnchor
|
||||||
|
)
|
||||||
|
cursor.setPosition(end + textBlock.position(), QTextCursor.MoveMode.KeepAnchor)
|
||||||
cursor.setCharFormat(def_format)
|
cursor.setCharFormat(def_format)
|
||||||
return
|
return
|
||||||
|
|
||||||
def display_definition(self) -> None:
|
def display_definition(self) -> None:
|
||||||
cursor = self.paraEdit.textCursor()
|
cursor = self.paraEdit.textCursor()
|
||||||
|
textBlock = self.paraEdit.document().findBlock(cursor.position())
|
||||||
|
blockNum = textBlock.blockNumber()
|
||||||
query = QSqlQuery()
|
query = QSqlQuery()
|
||||||
query.prepare(
|
query.prepare(
|
||||||
"SELECT w.* FROM word_paragraph wp "
|
"SELECT w.* FROM word_block wb "
|
||||||
"LEFT JOIN words w "
|
"LEFT JOIN words w "
|
||||||
"ON (w.word_id = wp.word_id) "
|
"ON (w.word_id = wb.word_id) "
|
||||||
"WHERE :position BETWEEN wp.start AND wp.end "
|
"WHERE :position BETWEEN wb.start AND wb.end "
|
||||||
"AND wp.paragraph_id = :paragraph_id"
|
"AND wb.block = :block AND wb.section_id = :section_id"
|
||||||
)
|
)
|
||||||
query.bindValue(":position", cursor.position())
|
query.bindValue(":position", cursor.position() - textBlock.position())
|
||||||
query.bindValue(":paragraph_id", self.paragraph_id)
|
query.bindValue(":block", blockNum)
|
||||||
print("display_definition()", cursor.position())
|
query.bindValue(":section_id", self.section_id)
|
||||||
if not query.exec():
|
if not query.exec():
|
||||||
query_error(query)
|
query_error(query)
|
||||||
if not query.next():
|
if not query.next():
|
||||||
@@ -237,15 +346,22 @@ class EditDialog(QDialog, Ui_Dialog):
|
|||||||
word_format.setFontWeight(QFont.Weight.Normal)
|
word_format.setFontWeight(QFont.Weight.Normal)
|
||||||
word_format.setFontPointSize(16)
|
word_format.setFontPointSize(16)
|
||||||
cursor.setCharFormat(word_format)
|
cursor.setCharFormat(word_format)
|
||||||
for key in definition.keys():
|
try:
|
||||||
|
cursor.insertBlock()
|
||||||
|
cursor.insertText(definition["phonetic"])
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
for meaning in definition["meanings"]:
|
||||||
cursor.insertList(typeFormat)
|
cursor.insertList(typeFormat)
|
||||||
cursor.insertText(key)
|
cursor.insertText(meaning["partOfSpeech"])
|
||||||
cursor.insertList(defFormat)
|
cursor.insertList(defFormat)
|
||||||
first = True
|
first = True
|
||||||
for a_def in definition[key]:
|
for a_def in meaning["definitions"]:
|
||||||
if not first:
|
if not first:
|
||||||
cursor.insertBlock()
|
cursor.insertBlock()
|
||||||
cursor.insertText(a_def)
|
cursor.insertText(a_def["definition"])
|
||||||
|
# TODO: synonyms
|
||||||
|
# TODO: antonyms
|
||||||
first = False
|
first = False
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -257,41 +373,42 @@ class EditDialog(QDialog, Ui_Dialog):
|
|||||||
self.stackedWidget.setCurrentIndex(1 - idx)
|
self.stackedWidget.setCurrentIndex(1 - idx)
|
||||||
return
|
return
|
||||||
|
|
||||||
def nextDefinition(self):
|
def nextDefinition(self) -> None:
|
||||||
cursor = self.paraEdit.textCursor()
|
cursor = self.paraEdit.textCursor()
|
||||||
position = cursor.position()
|
position = cursor.position()
|
||||||
print(position)
|
block = cursor.block()
|
||||||
formats = cursor.block().textFormats()
|
|
||||||
found = None
|
found = None
|
||||||
for f in formats:
|
while block and not found:
|
||||||
wc = QTextCursor(cursor)
|
formats = block.textFormats()
|
||||||
wc.setPosition(f.start)
|
for f in formats:
|
||||||
wc.movePosition(
|
wc = QTextCursor(cursor)
|
||||||
QTextCursor.MoveOperation.Right,
|
wc.setPosition(f.start)
|
||||||
QTextCursor.MoveMode.KeepAnchor,
|
wc.movePosition(
|
||||||
f.length,
|
QTextCursor.MoveOperation.Right,
|
||||||
)
|
QTextCursor.MoveMode.KeepAnchor,
|
||||||
word = wc.selectedText()
|
f.length,
|
||||||
cf = wc.charFormat()
|
)
|
||||||
if f.start <= position:
|
word = wc.selectedText()
|
||||||
continue
|
cf = wc.charFormat()
|
||||||
if not cf.fontUnderline():
|
if f.start <= position:
|
||||||
continue
|
continue
|
||||||
if not found:
|
if not cf.fontUnderline():
|
||||||
found = f
|
continue
|
||||||
elif f.start < found.start:
|
if not found:
|
||||||
found = f
|
found = f
|
||||||
if found:
|
elif f.start < found.start:
|
||||||
cursor.setPosition(found.start)
|
found = f
|
||||||
self.paraEdit.setTextCursor(cursor)
|
if found:
|
||||||
|
cursor.setPosition(found.start)
|
||||||
|
self.paraEdit.setTextCursor(cursor)
|
||||||
|
block = block.next()
|
||||||
self.display_definition()
|
self.display_definition()
|
||||||
return
|
return
|
||||||
|
|
||||||
def scrollTo(self, position):
|
def scrollTo(self, position: int) -> None:
|
||||||
cursor = self.paraEdit.textCursor()
|
cursor = self.paraEdit.textCursor()
|
||||||
cursor.setPosition(position)
|
cursor.setPosition(position)
|
||||||
rect = self.paraEdit.cursorRect(cursor)
|
rect = self.paraEdit.cursorRect(cursor)
|
||||||
print(rect)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def savePosition(self) -> None:
|
def savePosition(self) -> None:
|
||||||
|
|||||||
16
main.py
16
main.py
@@ -2,6 +2,7 @@
|
|||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
from typing import cast
|
||||||
|
|
||||||
from PyQt6.QtCore import Qt, pyqtSlot
|
from PyQt6.QtCore import Qt, pyqtSlot
|
||||||
from PyQt6.QtGui import (
|
from PyQt6.QtGui import (
|
||||||
@@ -42,15 +43,13 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||||||
model.setQuery(query)
|
model.setQuery(query)
|
||||||
self.peopleView.setModel(model)
|
self.peopleView.setModel(model)
|
||||||
self.peopleView.setModelColumn(1)
|
self.peopleView.setModelColumn(1)
|
||||||
# self.load_definition(word, PyDictionary.meaning(word))
|
|
||||||
# self.wordButton.clicked.connect(self.wordAction)
|
|
||||||
self.ReadButton.clicked.connect(self.readAction)
|
self.ReadButton.clicked.connect(self.readAction)
|
||||||
self.bookBtn.clicked.connect(self.bookAction)
|
self.bookBtn.clicked.connect(self.bookAction)
|
||||||
self.createActions()
|
self.createActions()
|
||||||
self.show()
|
self.show()
|
||||||
return
|
return
|
||||||
|
|
||||||
def createActions(self):
|
def createActions(self) -> None:
|
||||||
query = QSqlQuery()
|
query = QSqlQuery()
|
||||||
query.prepare("SELECT * FROM books ORDER BY title")
|
query.prepare("SELECT * FROM books ORDER BY title")
|
||||||
if not query.exec():
|
if not query.exec():
|
||||||
@@ -63,11 +62,9 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||||||
return
|
return
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def setBookAction(self):
|
def setBookAction(self) -> None:
|
||||||
action = self.sender()
|
action = cast(QAction, self.sender())
|
||||||
print(action)
|
|
||||||
book_id = action.data()
|
book_id = action.data()
|
||||||
print(book_id)
|
|
||||||
indexes = self.peopleView.selectedIndexes()
|
indexes = self.peopleView.selectedIndexes()
|
||||||
if len(indexes) < 1:
|
if len(indexes) < 1:
|
||||||
return
|
return
|
||||||
@@ -164,8 +161,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||||||
SQL_CMDS = [
|
SQL_CMDS = [
|
||||||
"PRAGMA foreign_keys=ON",
|
"PRAGMA foreign_keys=ON",
|
||||||
"CREATE TABLE IF NOT EXISTS words "
|
"CREATE TABLE IF NOT EXISTS words "
|
||||||
"(word_id INTEGER PRIMARY KEY AUTOINCREMENT, word TEXT, definition TEXT, "
|
"(word_id INTEGER PRIMARY KEY AUTOINCREMENT, word TEXT, definition TEXT)",
|
||||||
"translation TEXT)",
|
|
||||||
"CREATE TABLE IF NOT EXISTS books "
|
"CREATE TABLE IF NOT EXISTS books "
|
||||||
"(book_id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT, author TEXT, "
|
"(book_id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT, author TEXT, "
|
||||||
"uuid TEXT, level INTEGER)",
|
"uuid TEXT, level INTEGER)",
|
||||||
@@ -236,7 +232,7 @@ def schema_update(db: QSqlDatabase) -> None:
|
|||||||
if not query.exec(matches.group(1) + new_table_name + matches.group(3)):
|
if not query.exec(matches.group(1) + new_table_name + matches.group(3)):
|
||||||
query_error(query)
|
query_error(query)
|
||||||
# step 5 transfer content
|
# step 5 transfer content
|
||||||
coldefs = re.search(r"\((.+)\)", old).group(1).split(", ")
|
coldefs = re.search(r"\((.+)\)", old).group(1).split(", ") # type: ignore[union-attr]
|
||||||
cols = [x.split(" ")[0] for x in coldefs]
|
cols = [x.split(" ")[0] for x in coldefs]
|
||||||
if not query.exec(
|
if not query.exec(
|
||||||
"INSERT INTO "
|
"INSERT INTO "
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ class Ui_Dialog(object):
|
|||||||
self.paraEdit = QtWidgets.QTextEdit(parent=self.page)
|
self.paraEdit = QtWidgets.QTextEdit(parent=self.page)
|
||||||
font = QtGui.QFont()
|
font = QtGui.QFont()
|
||||||
font.setFamily("OpenDyslexic")
|
font.setFamily("OpenDyslexic")
|
||||||
font.setPointSize(16)
|
font.setPointSize(11)
|
||||||
self.paraEdit.setFont(font)
|
self.paraEdit.setFont(font)
|
||||||
self.paraEdit.setObjectName("paraEdit")
|
self.paraEdit.setObjectName("paraEdit")
|
||||||
self.verticalLayout_2.addWidget(self.paraEdit)
|
self.verticalLayout_2.addWidget(self.paraEdit)
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
<property name="font">
|
<property name="font">
|
||||||
<font>
|
<font>
|
||||||
<family>OpenDyslexic</family>
|
<family>OpenDyslexic</family>
|
||||||
<pointsize>16</pointsize>
|
<pointsize>11</pointsize>
|
||||||
</font>
|
</font>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
|
|||||||
Reference in New Issue
Block a user