Create a webpage version of a lesson

This commit is contained in:
Christopher T. Johnson
2023-11-20 13:23:05 -05:00
parent 217e31ee7a
commit d3c77f5569
17 changed files with 187 additions and 69 deletions

View File

@@ -1,4 +1,6 @@
main.py: ui/*.py
main.py: ui/*.py ui/resources.rcc
%.py:%.ui
pyuic6 $< >$@
ui/%.rcc:ui/%.qrc ui/*.css ui/*/*.otf
rcc --binary $< -o $@

View File

@@ -1,16 +1,29 @@
import json
import os
import re
from typing import cast
import requests
from PyQt6.QtCore import QPoint, QRect, Qt, QTimer, pyqtSlot
from PyQt6.QtCore import (
QFile,
QIODeviceBase,
QPoint,
QRect,
QResource,
Qt,
QTimer,
pyqtSlot,
)
from PyQt6.QtGui import (
QBrush,
QColor,
QFont,
QKeyEvent,
QMouseEvent,
QPainter,
QPainterPath,
QPaintEvent,
QTextBlockFormat,
QTextCharFormat,
QTextCursor,
QTextDocument,
@@ -29,21 +42,28 @@ class EditDialog(QDialog, Ui_Dialog):
def __init__(self, person_id: int) -> None:
super(EditDialog, self).__init__()
self.person_id = person_id
if not QResource.registerResource(
os.path.join(os.path.dirname(__file__), "../ui/resources.rcc"), "/"
):
raise Exception("Unable to register resources.rcc")
styleSheet = QResource(":/display.css").data().decode("utf-8")
self.setupUi(self)
#
# Override UI
#
font = QFont()
font.setFamily("OpenDyslexic")
font.setPointSize(14)
self.paraEdit.setFont(font)
self.printBtn = QPushButton()
self.printBtn.setText("Print")
self.printBtn.setObjectName("printBtn")
self.verticalLayout.addWidget(self.printBtn)
#
# End overrides
#
self.load_book(self.person_id)
blockNumber = self.block
self.paraEdit.setReadOnly(True)
self.paraEdit.document().setDefaultStyleSheet(styleSheet)
self.defEdit.setReadOnly(True)
self.defEdit.document().setDefaultStyleSheet(styleSheet)
self.show_section(self.section_id)
self.block = blockNumber
self.savePosition()
@@ -54,10 +74,72 @@ class EditDialog(QDialog, Ui_Dialog):
self.prevBtn.clicked.connect(self.prevAction)
self.nextParaBtn.clicked.connect(self.nextParaAction)
self.prevParaBtn.clicked.connect(self.prevParaAction)
self.printBtn.clicked.connect(self.printAction)
self.paraEdit.verticalScrollBar().valueChanged.connect(self.scrollSlot)
self.scrollBtn.clicked.connect(self.scrollAction)
return
def defToHtml(self, word: str, definition) -> str:
html = f'<h1 class="def-word">{word}</h1>' + "\n"
try:
html += f"<p class=\"phonetic\">{definition['phonetic']}</p>" + "\n"
except Exception:
pass
html += '<ul class="outer">' + "\n"
for meaning in definition["meanings"]:
html += f"<li>{meaning['partOfSpeech']}"
html += '<ul class="inner">'
for a_def in meaning["definitions"]:
html += f"<li>{a_def['definition']}</li>\n"
html += "</ul>\n"
html += "</ul>\n<p/>\n"
return html
@pyqtSlot()
def printAction(self) -> None:
html = "<!DOCTYPE html>\n<html>\n<head>\n"
html += (
'<link href="https://fonts.cdnfonts.com/css/open-dyslexic" rel="stylesheet">'
+ "\n"
)
html += (
'<link href="https://fonts.cdnfonts.com/css/tex-gyre-heros" rel="stylesheet">'
+ "\n"
)
html += '<style type="text/css">\n'
style = QResource(":/print.css").data().decode("utf-8")
html += style
html += "</style>\n</head>\n<body>\n"
query = QSqlQuery()
query.prepare(
"SELECT w.* FROM word_block wb "
"LEFT JOIN words w "
"ON (w.word_id = wb.word_id) "
"WHERE wb.section_id = :section_id "
"ORDER BY w.word"
)
query.bindValue(":section_id", self.section_id)
if not query.exec():
query_error(query)
while query.next():
word = query.value("word")
definition = json.loads(query.value("definition"))
html += self.defToHtml(word, definition)
html += "\n"
html += "<hr/>\n"
html += '<div class="text">' + "\n"
text = self.sections[self.section_map[self.section_id]]
text = re.sub(r"</?body>", "", text)
html += text
html += "\n</div>\n"
html += "\n</body>\n</html>\n"
qf = QFile("out.html")
if qf.open(QIODeviceBase.OpenModeFlag.WriteOnly):
qf.write(html.encode("utf-8"))
qf.close()
print("Printed!")
return
@pyqtSlot()
def scrollAction(self) -> None:
position = (
@@ -200,18 +282,26 @@ class EditDialog(QDialog, Ui_Dialog):
definition = json.loads(query.value("definition"))
try:
phonetic = definition["phonetic"]
except:
phonetic = ""
def_format.setToolTip(
f'<font size="24">{word}:<br/><font family="Tex Gyre Heros">{phonetic}</font></font>'
)
cursor.setCharFormat(def_format)
cursor.mergeCharFormat(def_format)
except:
pass
return
#
# Event handlers
#
def mousePressEvent(self, event: QMouseEvent | None) -> None:
return
def keyPressEvent(self, event: QKeyEvent) -> None:
print(event)
super().keyPressEvent(event)
return
def paintEvent(self, e: QPaintEvent | None) -> None:
idx = self.stackedWidget.currentIndex()
if idx > 0:
@@ -328,8 +418,7 @@ class EditDialog(QDialog, Ui_Dialog):
query.bindValue(":end", end)
if not query.exec():
query_error(query)
if query.next():
return
if not query.next():
query.prepare(
"INSERT INTO word_block VALUES "
"( :word_id, :section_id, :block, :start, :end)"
@@ -341,6 +430,7 @@ class EditDialog(QDialog, Ui_Dialog):
query.bindValue(":end", end)
if not query.exec():
query_error(query)
def_format = QTextCharFormat()
def_format.setFontUnderline(True)
cursor = QTextCursor(self.paraEdit.document())
@@ -351,7 +441,7 @@ class EditDialog(QDialog, Ui_Dialog):
cursor.setPosition(
end + textBlock.position(), QTextCursor.MoveMode.KeepAnchor
)
cursor.setCharFormat(def_format)
cursor.mergeCharFormat(def_format)
return
def display_definition(self) -> None:
@@ -377,47 +467,10 @@ class EditDialog(QDialog, Ui_Dialog):
definition = json.loads(query.value("definition"))
self.defEdit.document().clear()
cursor = self.defEdit.textCursor()
h1_size = 48
h1_weight = QFont.Weight.Bold
p_size = 16
p_weight = QFont.Weight.Normal
word_format = QTextCharFormat()
word_format.setFontPointSize(h1_size)
word_format.setFontWeight(h1_weight)
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(p_weight)
word_format.setFontPointSize(p_size)
phonetic_format = QTextCharFormat()
phonetic_format.setFontPointSize(32)
phonetic_format.setFontFamily("TeX Gyre Heros")
try:
cursor.insertBlock()
cursor.insertText(definition["phonetic"], phonetic_format)
except Exception:
pass
cursor.setCharFormat(word_format)
for meaning in definition["meanings"]:
cursor.insertList(typeFormat)
cursor.insertText(meaning["partOfSpeech"])
cursor.insertList(defFormat)
first = True
for a_def in meaning["definitions"]:
if not first:
cursor.insertBlock()
cursor.insertText(a_def["definition"])
# TODO: synonyms
# TODO: antonyms
first = False
cursor.insertHtml(self.defToHtml(word,definition))
cursor.setPosition(0)
self.defEdit.setTextCursor(cursor)
print(self.defEdit.document().toHtml())
return
@pyqtSlot()

16
main.py
View File

@@ -341,10 +341,18 @@ if __name__ == "__main__":
for fileName in os.listdir("ui"):
if not fileName.endswith(".py"):
continue
uiName = fileName[:-3] + ".ui"
if os.path.getmtime("ui/" + uiName) > os.path.getmtime(
"ui/" + fileName
):
uiName = "ui/" + fileName[:-3] + ".ui"
rccName = "ui/" + fileName[:-3] + ".qrc"
if not os.path.isfile(uiName) and not os.path.isfile(rccName):
outOfDate.append(filenName)
continue
if os.path.isfile(uiName) and os.path.getmtime(
uiName
) > os.path.getmtime("ui/" + fileName):
outOfDate.append(fileName)
if os.path.isfile(rccName) and os.path.getmtime(
rccName
) > os.path.getmtime("ui/" + fileName):
outOfDate.append(fileName)
if len(outOfDate) > 0:
print(f"UI out of date: {', '.join(outOfDate)}")

View File

@@ -118,6 +118,8 @@
</item>
</layout>
</widget>
<resources/>
<resources>
<include location="resources.qrc"/>
</resources>
<connections/>
</ui>

23
ui/display.css Normal file
View File

@@ -0,0 +1,23 @@
body {
font-family: "opendyslexic", sans-serif;
}
hr {
height: 2px;
border-width: 0;
}
h1 {
font-size: 48px;
font-weight: Bold;
}
h3 {
font-size: 40px;
font-weight: Normal;
}
p, li {
max-width: 66ch;
font-size: 24px;
}
p.phonetic {
font-family: "Tex Gyre Heros", sans-serif;
font-size: 32px;
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

23
ui/print.css Normal file
View File

@@ -0,0 +1,23 @@
body {
font-family: "Open-Dyslexic", sans-serif;
}
hr {
height: 2px;
border-width: 0;
}
h1 {
font-size: 48px;
font-weight: Bold;
}
h3 {
font-size: 40px;
font-weight: Normal;
}
p, li {
max-width: 66ch;
font-size: 24px;
}
p.phonetic {
font-family: "TexGyreHeros", sans-serif;
font-size: 32px;
}

7
ui/resources.qrc Normal file
View File

@@ -0,0 +1,7 @@
<RCC>
<qresource prefix="/">
<file>print.css</file>
<file>display.css</file>
<file>opendyslexic/OpenDyslexic-Regular.otf</file>
</qresource>
</RCC>

BIN
ui/resources.rcc Normal file

Binary file not shown.