diff --git a/lib/__init__.py b/lib/__init__.py index 3056e3a..8cbe3d7 100644 --- a/lib/__init__.py +++ b/lib/__init__.py @@ -2,4 +2,3 @@ from .books import Book from .person import PersonDialog from .read import EditDialog from .session import SessionDialog -from .sounds import SoundOff diff --git a/lib/preferences.py b/lib/preferences.py new file mode 100644 index 0000000..0e2db20 --- /dev/null +++ b/lib/preferences.py @@ -0,0 +1,95 @@ +import json +import os +from PyQt6.QtCore import Qt, pyqtSlot +from PyQt6.QtWidgets import QDialog, QListWidgetItem, QAbstractItemView +from PyQt6.QtMultimedia import QMediaDevices +from ui.Preferences import Ui_Dialog + +class Preferences(QDialog, Ui_Dialog): + _instance = None + def __new__(cls): + if cls._instance: + return cls._instance + cls._instance = super(Preferences, cls).__new__(cls) + return cls._instance + @pyqtSlot(int) + def done(self,r): + self.hide() + super().done(r) + return + @pyqtSlot() + def exec(self): + self.show() + super().exec() + return + @pyqtSlot() + def open(self): + self.show() + super().open() + return + + def __init__(self, *args, **kwargs): + super(Preferences, self).__init__(*args, **kwargs) + self.setupUi(self) + self.hide() + # + # Overrides + # + self.alertList.setSelectionMode(QAbstractItemView.SelectionMode.MultiSelection) + self.playerList.setSelectionMode(QAbstractItemView.SelectionMode.MultiSelection) + self.readerCombo.setEditable(False) + self.phoneticsCombo.setEditable(False) + # + # End OverRides + # + for index, output in enumerate(QMediaDevices.audioOutputs()): + identifier = output.id().data().decode("utf-8") + description = output.description() + self.alertList.addItem(description) + self.playerList.addItem(description) + self.setCurrent() + return + + def setCurrent(self): + if os.path.exists('preferences.json'): + with open('preferences.json','r') as f: + self.preferences = json.load(f) + else: + self.preferences = { + 'readerFont': 'OpenDyslexic', + 'phoneticFont': 'Gentium', + 'alertOutputs': [ 'default' ], + 'playerOutputs': [ 'Feed for virtual microphone' ] + } + for output in self.preferences['alertOutputs']: + if output == 'default': + output = QMediaDevices.defaultAudioOutput().description() + for item in self.alertList.findItems(output,Qt.MatchFlag.MatchExactly): + item.setSelected(True) + for output in self.preferences['playerOutputs']: + if output == 'default': + output = QMediaDevices.defaultAudioOutput().description() + for item in self.playerList.findItems(output,Qt.MatchFlag.MatchExactly ): + item.setSelected(True) + index = self.readerCombo.findText(self.preferences['readerFont']) + if index >= 0: + self.readerCombo.setCurrentIndex(index) + index = self.phoneticsCombo.findText(self.preferences['phoneticFont']) + if index >= 0: + self.phoneticsCombo.setCurrentIndex(index) + return + + def get(self, name:str =None): + if not name: + return self.preferences + return self.preferences[name] + + def accept(self): + self.preferences['readerFont'] = self.readerCombo.currentFont().family() + self.preferences['phoneticFont'] =self.phoneticsCombo.currentFont().family() + self.preferences['alertOutputs'] = [ x.data(Qt.ItemDataRole.DisplayRole) for x in self.alertList.selectedItems() ] + self.preferences['playerOutputs'] = [ x.data(Qt.ItemDataRole.DisplayRole) for x in self.playerList.selectedItems() ] + with open('preferences.json','w') as f: + json.dump(self.preferences,f,indent=2) + super().accept() + return diff --git a/lib/read.py b/lib/read.py index f17d455..d45da67 100644 --- a/lib/read.py +++ b/lib/read.py @@ -18,7 +18,6 @@ from PyQt6.QtCore import ( from PyQt6.QtGui import ( QBrush, QColor, - QFont, QKeyEvent, QMouseEvent, QPainter, @@ -35,21 +34,32 @@ from PyQt6.QtWidgets import QDialog, QPushButton from main import query_error from ui.EditDialog import Ui_Dialog - +from lib.preferences import Preferences +from lib.sounds import SoundOff class EditDialog(QDialog, Ui_Dialog): + playSound = pyqtSignal(str) block: int paragraphs = True sessionSignal = pyqtSignal() displayedWord = pyqtSignal(int) newParagraph = pyqtSignal(int, int) - + def __init__(self, parent, session, person_id: int) -> None: self.session = session super(EditDialog, self).__init__(parent) - print(self.parent()) self.person_id = person_id + self.preferences = Preferences().get() + self.sound = SoundOff() styleSheet = QResource(":/display.css").data().decode("utf-8") + print(styleSheet) + styleSheet = styleSheet.replace( + '{readerFont}',self.preferences['readerFont'] + ) + styleSheet = styleSheet.replace( + '{phoneticFont}',self.preferences['phoneticFont'] + ) + print(styleSheet) self.setupUi(self) # # Override UI @@ -75,8 +85,6 @@ class EditDialog(QDialog, Ui_Dialog): self.scrollBtn.clicked.connect(self.scrollAction) self.nextBtn.clicked.connect(self.nextAction) self.prevBtn.clicked.connect(self.prevAction) - # self.sessionSignal.connect(self.session.timerAction) - # self.sessionBtn.clicked.connect(self.sessionAction) self.sessionBtn.clicked.connect(self.session.timerAction) self.paraEdit.verticalScrollBar().valueChanged.connect(self.scrollSlot) self.defEdit.selectionChanged.connect(self.recursiveDef) @@ -85,6 +93,8 @@ class EditDialog(QDialog, Ui_Dialog): # self.displayedWord.connect(self.session.addWord) self.newParagraph.connect(self.session.addBlock) + self.playSound.connect(self.sound.playSound) + #self.alert.connect(self.sound.alert) return # @@ -161,14 +171,14 @@ class EditDialog(QDialog, Ui_Dialog): self.phonetics = data["phonetics"] else: self.phonetics = None - print("Checking for phonetics") if not self.phonetics: return - print("Looking for audio") + print("Searching for audio file") for entry in self.phonetics: if len(entry["audio"]) > 0: # self.parent().playAlert.emit() - self.parent().playSound.emit(entry["audio"]) + print(f"playing {entry['audio']}") + self.playSound.emit(entry["audio"]) return @pyqtSlot() diff --git a/lib/session.py b/lib/session.py index d3b0642..88a086f 100644 --- a/lib/session.py +++ b/lib/session.py @@ -114,7 +114,7 @@ class SessionDialog(QDialog, Ui_Dialog): query = QSqlQuery() query.prepare( "UPDATE sessions " - "SET start=:start , SET stop=:stop, SET notes=:notes " + "SET start=:start, stop=:stop, notes=:notes " "WHERE sesion_id = :session_id" ) query.bindValue(":session_id", self.session_id) diff --git a/lib/sounds.py b/lib/sounds.py index bd37cfe..a891153 100644 --- a/lib/sounds.py +++ b/lib/sounds.py @@ -11,6 +11,13 @@ from PyQt6.QtMultimedia import ( class SoundOff(QObject): + _instance = None + def __new__(cls): + if cls._instance: + return cls._instance + cls._instance = super(SoundOff, cls).__new__(cls) + return cls._instance + def __init__(self): super().__init__() # @@ -95,7 +102,6 @@ class SoundOff(QObject): self.localPlayer.play() if not self.virtualDevice: return - return self.virtualPlayer.setSource(src) self.virtualPlayer.setPosition(0) if not self.virtualPlayer.audioOutput(): diff --git a/main.py b/main.py index 48f59b2..6024138 100755 --- a/main.py +++ b/main.py @@ -44,6 +44,7 @@ from PyQt6.QtWidgets import ( ) from lib import * +from lib.preferences import Preferences from ui.MainWindow import Ui_MainWindow @@ -60,8 +61,6 @@ def query_error(query: QSqlQuery) -> None: class MainWindow(QMainWindow, Ui_MainWindow): - playAlert = pyqtSignal() - playSound = pyqtSignal(str) def __init__(self) -> None: super(MainWindow, self).__init__() @@ -73,8 +72,7 @@ class MainWindow(QMainWindow, Ui_MainWindow): os.path.join(os.path.dirname(__file__), "ui/resources.rcc"), "/" ): raise Exception("Unable to register resources.rcc") - self.soundOff = SoundOff() - + model = QSqlQueryModel() query = QSqlQuery("SELECT * FROM people ORDER BY name") model.setQuery(query) @@ -86,23 +84,28 @@ class MainWindow(QMainWindow, Ui_MainWindow): # # Action Connections # - self.actionQuit.triggered.connect(self.close) # Y - self.actionAddBook.triggered.connect(self.addBook) # Y - self.actionEditBook.triggered.connect(self.editBook) # Y - self.actionRead.triggered.connect(self.readBook) # Y + self.actionQuit.triggered.connect(self.close) + self.actionAddBook.triggered.connect(self.addBook) + self.actionEditBook.triggered.connect(self.editBook) + self.actionRead.triggered.connect(self.readBook) self.actionRead.enabledChanged.connect(self.readBtn.setEnabled) - self.actionAddPerson.triggered.connect(self.addPerson) # Y - self.actionEditPerson.triggered.connect(self.editPerson) # Y + self.actionAddPerson.triggered.connect(self.addPerson) + self.actionEditPerson.triggered.connect(self.editPerson) self.actionEditPerson.enabledChanged.connect( self.editBtn.setEnabled - ) # Y - self.peopleView.doubleClicked.connect(self.editPerson) # Y - self.peopleView.clicked.connect(self.selectedPerson) # Y - self.playAlert.connect(self.soundOff.alert) - self.playSound.connect(self.soundOff.playSound) + ) + self.actionPreferences.triggered.connect(self.editPreferences) + self.peopleView.doubleClicked.connect(self.editPerson) + self.peopleView.clicked.connect(self.selectedPerson) self.show() return + @pyqtSlot() + def editPreferences(self): + dlg = Preferences() + dlg.exec() + return + @pyqtSlot(QModelIndex) def selectedPerson(self, index: QModelIndex) -> None: person_id = index.siblingAtColumn(0).data() diff --git a/ui/MainWindow.py b/ui/MainWindow.py index c910b0b..6d41ceb 100644 --- a/ui/MainWindow.py +++ b/ui/MainWindow.py @@ -46,7 +46,7 @@ class Ui_MainWindow(object): self.horizontalLayout.addWidget(self.widget) MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtWidgets.QMenuBar(parent=MainWindow) - self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 32)) + self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 24)) self.menubar.setObjectName("menubar") self.menuFile = QtWidgets.QMenu(parent=self.menubar) self.menuFile.setObjectName("menuFile") @@ -72,6 +72,8 @@ class Ui_MainWindow(object): self.actionEditPerson.setObjectName("actionEditPerson") self.actionRead = QtGui.QAction(parent=MainWindow) self.actionRead.setObjectName("actionRead") + self.actionPreferences = QtGui.QAction(parent=MainWindow) + self.actionPreferences.setObjectName("actionPreferences") self.menuFile.addAction(self.actionQuit) self.menuBooks_2.addAction(self.actionAddBook) self.menuBooks_2.addAction(self.actionEditBook) @@ -79,6 +81,7 @@ class Ui_MainWindow(object): self.menuPeople.addAction(self.actionEditPerson) self.menuBooks.addAction(self.menuBooks_2.menuAction()) self.menuBooks.addAction(self.menuPeople.menuAction()) + self.menuBooks.addAction(self.actionPreferences) self.menubar.addAction(self.menuFile.menuAction()) self.menubar.addAction(self.menuBooks.menuAction()) @@ -111,3 +114,4 @@ class Ui_MainWindow(object): self.actionEditPerson.setToolTip(_translate("MainWindow", "Edit A Person")) self.actionRead.setText(_translate("MainWindow", "Read")) self.actionRead.setToolTip(_translate("MainWindow", "Read Book")) + self.actionPreferences.setText(_translate("MainWindow", "Preferences")) diff --git a/ui/MainWindow.ui b/ui/MainWindow.ui index af4e159..933d7a1 100644 --- a/ui/MainWindow.ui +++ b/ui/MainWindow.ui @@ -80,7 +80,7 @@ 0 0 800 - 32 + 24 @@ -109,6 +109,7 @@ + @@ -159,6 +160,11 @@ Read Book + + + Preferences + + diff --git a/ui/Preferences.py b/ui/Preferences.py new file mode 100644 index 0000000..2dd6da4 --- /dev/null +++ b/ui/Preferences.py @@ -0,0 +1,62 @@ +# Form implementation generated from reading ui file 'ui/Preferences.ui' +# +# Created by: PyQt6 UI code generator 6.6.0 +# +# WARNING: Any manual changes made to this file will be lost when pyuic6 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt6 import QtCore, QtGui, QtWidgets + + +class Ui_Dialog(object): + def setupUi(self, Dialog): + Dialog.setObjectName("Dialog") + Dialog.resize(601, 265) + self.verticalLayout = QtWidgets.QVBoxLayout(Dialog) + self.verticalLayout.setObjectName("verticalLayout") + self.formLayout = QtWidgets.QFormLayout() + self.formLayout.setObjectName("formLayout") + self.label = QtWidgets.QLabel(parent=Dialog) + self.label.setObjectName("label") + self.formLayout.setWidget(0, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label) + self.readerCombo = QtWidgets.QFontComboBox(parent=Dialog) + self.readerCombo.setObjectName("readerCombo") + self.formLayout.setWidget(0, QtWidgets.QFormLayout.ItemRole.FieldRole, self.readerCombo) + self.label_2 = QtWidgets.QLabel(parent=Dialog) + self.label_2.setObjectName("label_2") + self.formLayout.setWidget(1, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_2) + self.phoneticsCombo = QtWidgets.QFontComboBox(parent=Dialog) + self.phoneticsCombo.setObjectName("phoneticsCombo") + self.formLayout.setWidget(1, QtWidgets.QFormLayout.ItemRole.FieldRole, self.phoneticsCombo) + self.label_3 = QtWidgets.QLabel(parent=Dialog) + self.label_3.setObjectName("label_3") + self.formLayout.setWidget(2, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_3) + self.alertList = QtWidgets.QListWidget(parent=Dialog) + self.alertList.setObjectName("alertList") + self.formLayout.setWidget(2, QtWidgets.QFormLayout.ItemRole.FieldRole, self.alertList) + self.label_4 = QtWidgets.QLabel(parent=Dialog) + self.label_4.setObjectName("label_4") + self.formLayout.setWidget(3, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_4) + self.playerList = QtWidgets.QListWidget(parent=Dialog) + self.playerList.setObjectName("playerList") + self.formLayout.setWidget(3, QtWidgets.QFormLayout.ItemRole.FieldRole, self.playerList) + self.verticalLayout.addLayout(self.formLayout) + self.buttonBox = QtWidgets.QDialogButtonBox(parent=Dialog) + self.buttonBox.setOrientation(QtCore.Qt.Orientation.Horizontal) + self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.StandardButton.Cancel|QtWidgets.QDialogButtonBox.StandardButton.Ok) + self.buttonBox.setObjectName("buttonBox") + self.verticalLayout.addWidget(self.buttonBox) + + self.retranslateUi(Dialog) + self.buttonBox.accepted.connect(Dialog.accept) # type: ignore + self.buttonBox.rejected.connect(Dialog.reject) # type: ignore + QtCore.QMetaObject.connectSlotsByName(Dialog) + + def retranslateUi(self, Dialog): + _translate = QtCore.QCoreApplication.translate + Dialog.setWindowTitle(_translate("Dialog", "Dialog")) + self.label.setText(_translate("Dialog", "Reader")) + self.label_2.setText(_translate("Dialog", "Phonetics")) + self.label_3.setText(_translate("Dialog", "Alert Outputs")) + self.label_4.setText(_translate("Dialog", "Player Outputs")) diff --git a/ui/Preferences.ui b/ui/Preferences.ui new file mode 100644 index 0000000..16f5f11 --- /dev/null +++ b/ui/Preferences.ui @@ -0,0 +1,109 @@ + + + Dialog + + + + 0 + 0 + 601 + 265 + + + + Dialog + + + + + + + + Reader + + + + + + + + + + Phonetics + + + + + + + + + + Alert Outputs + + + + + + + + + + Player Outputs + + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + Dialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + Dialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/ui/display.css b/ui/display.css index 0227e23..5baf7a0 100644 --- a/ui/display.css +++ b/ui/display.css @@ -1,5 +1,5 @@ body { - font-family: "opendyslexic", sans-serif; + font-family: "{readerFont}", sans-serif; } hr { height: 2px; @@ -18,6 +18,6 @@ p, li { font-size: 24px; } p.phonetic { - font-family: "Gentium", sans-serif; + font-family: "{phoneticFont}", sans-serif; font-size: 40px; } diff --git a/ui/resources.rcc b/ui/resources.rcc index d584d54..30067ce 100644 Binary files a/ui/resources.rcc and b/ui/resources.rcc differ