From 11c4801b7f925d4aeda552deecedfcd484d3ff38 Mon Sep 17 00:00:00 2001 From: "Christopher T. Johnson" Date: Fri, 15 Dec 2023 10:25:52 -0500 Subject: [PATCH] checkpoint prior to class --- lib/person.py | 25 ++++++-- lib/read.py | 150 +++++++++++++++++++++++++++++++++-------------- lib/session.py | 49 +++++++++++++++- main.py | 3 +- ui/EditDialog.py | 8 +-- ui/EditDialog.ui | 4 +- ui/beep.wav | Bin 0 -> 348158 bytes 7 files changed, 181 insertions(+), 58 deletions(-) create mode 100644 ui/beep.wav diff --git a/lib/person.py b/lib/person.py index 17f2b0a..701b3dc 100644 --- a/lib/person.py +++ b/lib/person.py @@ -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 = "\nHello\n" + title = self.sessionCombo.currentText() + html = f"\n{title}\n" html += ( '\n" ) html += "\n" + html += f"

{title}

\n" html += self.makeDefinitions() html += self.makeText() html += "\n\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 " - 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: diff --git a/lib/read.py b/lib/read.py index ff81dcb..fdfe870 100644 --- a/lib/read.py +++ b/lib/read.py @@ -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 = "\n\n\n" - html += ( - '' - + "\n" - ) - html += ( - '' - + "\n" - ) - html += '\n\n\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 += "
\n" - html += '
' + "\n" - text = self.sections[self.section_map[self.section_id]] - text = re.sub(r"", "", text) - html += text - html += "\n
\n" - html += "\n\n\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'

{word}

' + "\n" try: - html += f"

{definition['phonetic']}

" + "\n" + words = [] + for phonetic in definition["phonetics"]: + if phonetic["text"] in words: + continue + words.append(phonetic["text"]) + html += f'

{phonetic["text"]}

' + "\n" except Exception: pass html += '