Hello\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"?body>", "", 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 += '
' + "\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))
diff --git a/lib/session.py b/lib/session.py
index d7dc89f..5fff91a 100644
--- a/lib/session.py
+++ b/lib/session.py
@@ -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),
diff --git a/main.py b/main.py
index 15031c0..071108f 100755
--- a/main.py
+++ b/main.py
@@ -203,7 +203,8 @@ SQL_CMDS = [
#
"CREATE TABLE IF NOT EXISTS session_word "
"(session_id INTEGER REFERENCES sessions ON DELETE CASCADE, "
- "word_id INTEGER REFERENCES words ON DELETE CASCADE)",
+ "word_id INTEGER REFERENCES words ON DELETE CASCADE, "
+ "important INTEGER DEFAULT 0)",
#
"CREATE TABLE IF NOT EXISTS session_block "
"(session_id INTEGER REFERENCES sessions ON DELETE CASCADE, "
diff --git a/ui/EditDialog.py b/ui/EditDialog.py
index d412b2d..f4b1ee6 100644
--- a/ui/EditDialog.py
+++ b/ui/EditDialog.py
@@ -48,9 +48,9 @@ class Ui_Dialog(object):
self.defineBtn = QtWidgets.QPushButton(parent=self.widget)
self.defineBtn.setObjectName("defineBtn")
self.verticalLayout.addWidget(self.defineBtn)
- self.printBtn = QtWidgets.QPushButton(parent=self.widget)
- self.printBtn.setObjectName("printBtn")
- self.verticalLayout.addWidget(self.printBtn)
+ self.playBtn = QtWidgets.QPushButton(parent=self.widget)
+ self.playBtn.setObjectName("playBtn")
+ self.verticalLayout.addWidget(self.playBtn)
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding)
self.verticalLayout.addItem(spacerItem)
self.scrollBtn = QtWidgets.QPushButton(parent=self.widget)
@@ -80,7 +80,7 @@ class Ui_Dialog(object):
_translate = QtCore.QCoreApplication.translate
Dialog.setWindowTitle(_translate("Dialog", "Reader"))
self.defineBtn.setText(_translate("Dialog", "Show Def"))
- self.printBtn.setText(_translate("Dialog", "Print"))
+ self.playBtn.setText(_translate("Dialog", "Play"))
self.scrollBtn.setText(_translate("Dialog", "Scroll"))
self.nextBtn.setText(_translate("Dialog", "Next Para"))
self.prevBtn.setText(_translate("Dialog", "Prev Para"))
diff --git a/ui/EditDialog.ui b/ui/EditDialog.ui
index dcadad8..42fe263 100644
--- a/ui/EditDialog.ui
+++ b/ui/EditDialog.ui
@@ -59,9 +59,9 @@
-
+
- Print
+ Play
diff --git a/ui/beep.wav b/ui/beep.wav
new file mode 100644
index 0000000..54ce4b9
Binary files /dev/null and b/ui/beep.wav differ