checkpoint prior to class

This commit is contained in:
Christopher T. Johnson
2023-12-15 10:25:52 -05:00
parent a03289db51
commit 11c4801b7f
7 changed files with 181 additions and 58 deletions

View File

@@ -8,8 +8,10 @@ from email.message import EmailMessage
from html.parser import HTMLParser
from io import StringIO
from PyQt6.QtCore import QResource, QSize, Qt, pyqtSlot
import css_inline
from PyQt6.QtCore import QResource, QSize, Qt, QUrl, pyqtSlot
from PyQt6.QtGui import QStandardItem, QStandardItemModel
from PyQt6.QtMultimedia import QMediaDevices, QSoundEffect
from PyQt6.QtSql import QSqlQuery, QSqlQueryModel
from PyQt6.QtWidgets import QDialog, QDialogButtonBox, QStyledItemDelegate
@@ -106,6 +108,7 @@ class PersonDialog(QDialog, Ui_Dialog):
SectionSequenceRole = Qt.ItemDataRole.UserRole + 1
BookIdRole = Qt.ItemDataRole.UserRole + 2
person_id = 0
inliner = css_inline.CSSInliner(keep_style_tags=True, keep_link_tags=True)
def __init__(self, *args, **kwargs):
self.person_id = kwargs.pop("person_id", 0)
@@ -202,27 +205,41 @@ class PersonDialog(QDialog, Ui_Dialog):
@pyqtSlot()
def senditAction(self) -> None:
html = "<!DOCTYPE html>\n<html><head><title>Hello</title>\n"
title = self.sessionCombo.currentText()
html = f"<!DOCTYPE html>\n<html><head><title>{title}</title>\n"
html += (
'<style type="text/css">\n'
+ QResource(":email.css").data().decode("utf-8")
+ "</style>\n"
)
html += "</head><body>\n"
html += f"<h1>{title}</h1>\n"
html += self.makeDefinitions()
html += self.makeText()
html += "</body>\n</html>\n"
if self.sender() == self.printBtn:
dev = None
for output in QMediaDevices.audioOutputs():
if output.id().data().decode("UTF-8") == "virt-input":
dev = output
break
self.alert = QSoundEffect()
if dev:
self.alert.setAudioDevice(dev)
self.alert.setSource(QUrl.fromLocalFile("ui/beep.wav"))
self.alert.setLoopCount(1)
self.alert.play()
print(html)
return
msg = EmailMessage(policy=policy.default)
start = datetime.fromisoformat(self.sessionCombo.currentText())
msg["Subject"] = f"TT English, Session: {start.date().isoformat()}"
msg["From"] = "Christopher T. Johnson <cjohnson@troglodite.com>"
msg["To"] = self.emailEdit.text().strip()
# msg["To"] = self.emailEdit.text().strip()
msg["To"] = "cjohnson@troglodite.com"
msg.set_content("There is a html message you should read")
msg.add_alternative(html, subtype="html")
msg.add_alternative(self.inliner.inline(html), subtype="html")
server = smtplib.SMTP(secrets.SMTP_HOST, secrets.SMTP_PORT)
server.set_debuglevel(1)
if secrets.SMTP_STARTTLS:

View File

@@ -12,6 +12,7 @@ from PyQt6.QtCore import (
QResource,
Qt,
QTimer,
QUrl,
pyqtSignal,
pyqtSlot,
)
@@ -30,6 +31,7 @@ from PyQt6.QtGui import (
QTextDocument,
QTextListFormat,
)
from PyQt6.QtMultimedia import QAudioOutput, QMediaDevices, QMediaPlayer
from PyQt6.QtSql import QSqlDatabase, QSqlQuery, QSqlQueryModel
from PyQt6.QtWidgets import QDialog, QPushButton
@@ -43,6 +45,7 @@ class EditDialog(QDialog, Ui_Dialog):
sessionSignal = pyqtSignal()
displayedWord = pyqtSignal(int)
newParagraph = pyqtSignal(int, int)
soundEffect = QMediaPlayer()
def __init__(self, session, person_id: int) -> None:
self.session = session
@@ -60,6 +63,16 @@ class EditDialog(QDialog, Ui_Dialog):
#
# End overrides
#
audioOutput = QAudioOutput()
dev = None
for output in QMediaDevices.audioOutputs():
if output.id().data().decode("UTF-8") == "virt-input":
dev = output
break
if dev:
audioOutput.setDevice(dev)
self.audioOutput = audioOutput
self.soundEffect.setAudioOutput(audioOutput)
self.load_book(self.person_id)
blockNumber = self.block
self.paraEdit.setReadOnly(True)
@@ -74,7 +87,7 @@ class EditDialog(QDialog, Ui_Dialog):
# Connect widgets
#
self.defineBtn.clicked.connect(self.defineAction)
self.printBtn.clicked.connect(self.printAction)
self.playBtn.clicked.connect(self.playAction)
self.scrollBtn.clicked.connect(self.scrollAction)
self.nextBtn.clicked.connect(self.nextAction)
self.prevBtn.clicked.connect(self.prevAction)
@@ -87,11 +100,46 @@ class EditDialog(QDialog, Ui_Dialog):
#
self.displayedWord.connect(self.session.addWord)
self.newParagraph.connect(self.session.addBlock)
self.soundEffect.errorOccurred.connect(self.mediaError)
self.soundEffect.playbackStateChanged.connect(self.changedState)
self.soundEffect.mediaStatusChanged.connect(self.changedStatus)
return
#
# slots
#
@pyqtSlot(QMediaPlayer.MediaStatus)
def changedStatus(self, status):
if status == QMediaPlayer.MediaStatus.LoadedMedia:
self.soundEffect.play()
audioOutput = self.soundEffect.audioOutput()
if not audioOutput:
self.soundEffect.setAudioOutput(self.audioOutput)
audioOutput = self.audioOutput
print(f"{status} No audioOutput???")
audioDevice = audioOutput.device()
print(audioDevice.description(), audioDevice.id().data())
print(status)
return
@pyqtSlot(QMediaPlayer.PlaybackState)
def changedState(self, status):
audioOutput = self.soundEffect.audioOutput()
if not audioOutput:
print(f"{status} No AudioOutput")
return
audioDevice = audioOutput.device()
print(audioDevice.description(), audioDevice.id().data())
print(status)
return
@pyqtSlot(QMediaPlayer.Error, str)
def mediaError(self, error, string):
print(error)
print(string)
return
@pyqtSlot()
def sessionAction(self) -> None:
self.sessionSignal.emit()
@@ -99,48 +147,53 @@ class EditDialog(QDialog, Ui_Dialog):
return
@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 COLLATE NOCASE"
)
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!")
def playAction(self) -> None:
print("playAction")
idx = self.stackedWidget.currentIndex()
if idx == 0: # Reading
# find word
cursor = self.paraEdit.textCursor()
fmt = cursor.charFormat()
if not fmt.fontUnderline():
self.addword()
cursor = self.paraEdit.textCursor()
textBlock = self.paraEdit.document().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
print("Checking for phonetics")
if not self.phonetics:
return
print("Looking for audio")
for entry in self.phonetics:
if len(entry["audio"]) > 0:
self.soundEffect.setSource(QUrl(entry["audio"]))
if (
self.soundEffect.mediaStatus()
== QMediaPlayer.MediaStatus.LoadedMedia
):
self.soundEffect.play()
return
return
@pyqtSlot()
@@ -230,7 +283,12 @@ class EditDialog(QDialog, Ui_Dialog):
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"
words = []
for phonetic in definition["phonetics"]:
if phonetic["text"] in words:
continue
words.append(phonetic["text"])
html += f'<p class="phonetic">{phonetic["text"]}</p>' + "\n"
except Exception:
pass
html += '<ul class="outer">' + "\n"
@@ -528,6 +586,10 @@ class EditDialog(QDialog, Ui_Dialog):
return
word = query.value("word")
definition = json.loads(query.value("definition"))
if "phonetics" in definition:
self.phonetics = definition["phonetics"]
else:
self.phonetics = None
self.defEdit.document().clear()
cursor = self.defEdit.textCursor()
cursor.insertHtml(self.defToHtml(word, definition))

View File

@@ -1,7 +1,9 @@
from datetime import datetime, timedelta
from PyQt6.QtCore import Qt, QTime, QTimer, pyqtSignal, pyqtSlot
from PyQt6.QtCore import QModelIndex, Qt, QTime, QTimer, pyqtSignal, pyqtSlot
from PyQt6.QtGui import (
QBrush,
QPalette,
QStandardItem,
QStandardItemModel,
QTextBlockFormat,
@@ -19,6 +21,7 @@ class SessionDialog(QDialog, Ui_Dialog):
WordIdRole = Qt.ItemDataRole.UserRole
SectionIdRole = Qt.ItemDataRole.UserRole + 1
BlockRole = Qt.ItemDataRole.UserRole + 2
WordImportantRole = Qt.ItemDataRole.UserRole + 3
timer = QTimer()
startTime = datetime.now()
@@ -37,6 +40,22 @@ class SessionDialog(QDialog, Ui_Dialog):
#
self.timer.timeout.connect(self.tickAction)
self.activeBox.stateChanged.connect(self.activeAction)
self.wordView.doubleClicked.connect(self.wordSelected)
return
@pyqtSlot(QModelIndex)
def wordSelected(self, index: QModelIndex) -> None:
flag = index.data(SessionDialog.WordImportantRole)
flag = 1 - flag
model = index.model()
model.setData(index, flag, SessionDialog.WordImportantRole)
item = model.itemFromIndex(index)
if flag:
item.setForeground(Qt.GlobalColor.red)
else:
item.setForeground(
self.wordView.palette().color(self.wordView.foregroundRole())
)
return
@pyqtSlot()
@@ -63,6 +82,26 @@ class SessionDialog(QDialog, Ui_Dialog):
self.wordView.model().clear()
return
@pyqtSlot()
def rejected(self) -> None:
msg = QMessageBox()
msg.setText("There is unsaved data.")
msg.setInformativeText("Do you want to save the session?")
msg.setStandardButtons(
QMessageBox.StandardButton.Save
| QMessageBox.StandardButton.Discard
| QMessageBox.StandardButton.Cancel
)
ret = msg.exec()
if ret == QMessageBox.StandardButton.Cancel:
return
if ret == QMessageBox.StandardButton.Discard:
super().reject()
return
self.accept()
self.done()
return
@pyqtSlot()
def accept(self) -> None:
if not self.sessionStart:
@@ -85,13 +124,16 @@ class SessionDialog(QDialog, Ui_Dialog):
query_error(query)
session_id = query.lastInsertId()
model = self.wordView.model()
sql = "INSERT INTO session_word (session_id,word_id) VALUES "
parameters = ["(?,?)" for x in range(model.rowCount())]
sql = "INSERT INTO session_word (session_id,word_id, important) VALUES "
parameters = ["(?,?,?)" for x in range(model.rowCount())]
sql += ", ".join(parameters)
query.prepare(sql)
for row in range(model.rowCount()):
query.addBindValue(session_id)
query.addBindValue(model.item(row).data(SessionDialog.WordIdRole))
query.addBindValue(
model.item(row).data(SessionDialog.WordImportantRole)
)
if not query.exec():
query_error(query)
sql = (
@@ -156,6 +198,7 @@ class SessionDialog(QDialog, Ui_Dialog):
word = QStandardItem()
word.setData(query.value("word"), Qt.ItemDataRole.DisplayRole)
word.setData(query.value("word_id"), SessionDialog.WordIdRole)
word.setData(0, SessionDialog.WordImportantRole)
model = self.wordView.model()
matches = model.match(
model.createIndex(0, 0),