Add Sound Module
This commit is contained in:
@@ -2,3 +2,4 @@ from .books import Book
|
|||||||
from .person import PersonDialog
|
from .person import PersonDialog
|
||||||
from .read import EditDialog
|
from .read import EditDialog
|
||||||
from .session import SessionDialog
|
from .session import SessionDialog
|
||||||
|
from .sounds import SoundOff
|
||||||
|
|||||||
62
lib/read.py
62
lib/read.py
@@ -1,5 +1,4 @@
|
|||||||
import json
|
import json
|
||||||
import os
|
|
||||||
import re
|
import re
|
||||||
from typing import cast
|
from typing import cast
|
||||||
|
|
||||||
@@ -31,7 +30,6 @@ from PyQt6.QtGui import (
|
|||||||
QTextDocument,
|
QTextDocument,
|
||||||
QTextListFormat,
|
QTextListFormat,
|
||||||
)
|
)
|
||||||
from PyQt6.QtMultimedia import QAudioOutput, QMediaDevices, QMediaPlayer
|
|
||||||
from PyQt6.QtSql import QSqlDatabase, QSqlQuery, QSqlQueryModel
|
from PyQt6.QtSql import QSqlDatabase, QSqlQuery, QSqlQueryModel
|
||||||
from PyQt6.QtWidgets import QDialog, QPushButton
|
from PyQt6.QtWidgets import QDialog, QPushButton
|
||||||
|
|
||||||
@@ -45,16 +43,12 @@ class EditDialog(QDialog, Ui_Dialog):
|
|||||||
sessionSignal = pyqtSignal()
|
sessionSignal = pyqtSignal()
|
||||||
displayedWord = pyqtSignal(int)
|
displayedWord = pyqtSignal(int)
|
||||||
newParagraph = pyqtSignal(int, int)
|
newParagraph = pyqtSignal(int, int)
|
||||||
soundEffect = QMediaPlayer()
|
|
||||||
|
|
||||||
def __init__(self, session, person_id: int) -> None:
|
def __init__(self, parent, session, person_id: int) -> None:
|
||||||
self.session = session
|
self.session = session
|
||||||
super(EditDialog, self).__init__()
|
super(EditDialog, self).__init__(parent)
|
||||||
|
print(self.parent())
|
||||||
self.person_id = person_id
|
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")
|
styleSheet = QResource(":/display.css").data().decode("utf-8")
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
#
|
#
|
||||||
@@ -63,16 +57,6 @@ class EditDialog(QDialog, Ui_Dialog):
|
|||||||
#
|
#
|
||||||
# End overrides
|
# 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)
|
self.load_book(self.person_id)
|
||||||
blockNumber = self.block
|
blockNumber = self.block
|
||||||
self.paraEdit.setReadOnly(True)
|
self.paraEdit.setReadOnly(True)
|
||||||
@@ -101,10 +85,6 @@ class EditDialog(QDialog, Ui_Dialog):
|
|||||||
#
|
#
|
||||||
self.displayedWord.connect(self.session.addWord)
|
self.displayedWord.connect(self.session.addWord)
|
||||||
self.newParagraph.connect(self.session.addBlock)
|
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
|
return
|
||||||
|
|
||||||
#
|
#
|
||||||
@@ -140,33 +120,6 @@ class EditDialog(QDialog, Ui_Dialog):
|
|||||||
self.setDefEdit(selection, word_id, definition)
|
self.setDefEdit(selection, word_id, definition)
|
||||||
return
|
return
|
||||||
|
|
||||||
@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
|
|
||||||
audioDevice = audioOutput.device()
|
|
||||||
print(status)
|
|
||||||
return
|
|
||||||
|
|
||||||
@pyqtSlot(QMediaPlayer.PlaybackState)
|
|
||||||
def changedState(self, status):
|
|
||||||
audioOutput = self.soundEffect.audioOutput()
|
|
||||||
if not audioOutput:
|
|
||||||
return
|
|
||||||
audioDevice = audioOutput.device()
|
|
||||||
print(status)
|
|
||||||
return
|
|
||||||
|
|
||||||
@pyqtSlot(QMediaPlayer.Error, str)
|
|
||||||
def mediaError(self, error, string):
|
|
||||||
print(error)
|
|
||||||
print(string)
|
|
||||||
return
|
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def sessionAction(self) -> None:
|
def sessionAction(self) -> None:
|
||||||
self.sessionSignal.emit()
|
self.sessionSignal.emit()
|
||||||
@@ -214,13 +167,8 @@ class EditDialog(QDialog, Ui_Dialog):
|
|||||||
print("Looking for audio")
|
print("Looking for audio")
|
||||||
for entry in self.phonetics:
|
for entry in self.phonetics:
|
||||||
if len(entry["audio"]) > 0:
|
if len(entry["audio"]) > 0:
|
||||||
self.soundEffect.setSource(QUrl(entry["audio"]))
|
# self.parent().playAlert.emit()
|
||||||
if (
|
self.parent().playSound.emit(entry["audio"])
|
||||||
self.soundEffect.mediaStatus()
|
|
||||||
== QMediaPlayer.MediaStatus.LoadedMedia
|
|
||||||
):
|
|
||||||
self.soundEffect.play()
|
|
||||||
return
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ class SessionDialog(QDialog, Ui_Dialog):
|
|||||||
sessionStart = None
|
sessionStart = None
|
||||||
sessionEnd = None
|
sessionEnd = None
|
||||||
blocks = QStandardItemModel()
|
blocks = QStandardItemModel()
|
||||||
|
session_id = -1
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
super(SessionDialog, self).__init__()
|
super(SessionDialog, self).__init__()
|
||||||
@@ -112,41 +113,14 @@ class SessionDialog(QDialog, Ui_Dialog):
|
|||||||
self.sessionEnd = datetime.now()
|
self.sessionEnd = datetime.now()
|
||||||
query = QSqlQuery()
|
query = QSqlQuery()
|
||||||
query.prepare(
|
query.prepare(
|
||||||
"INSERT INTO sessions "
|
"UPDATE sessions "
|
||||||
"(person_id, start, stop, notes) "
|
"SET start=:start , SET stop=:stop, SET notes=:notes "
|
||||||
"VALUES (:person_id, :start, :stop, :notes)"
|
"WHERE sesion_id = :session_id"
|
||||||
)
|
)
|
||||||
query.bindValue(":person_id", self.person_id)
|
query.bindValue(":session_id", self.session_id)
|
||||||
query.bindValue(":start", self.sessionStart.isoformat())
|
query.bindValue(":start", self.sessionStart.isoformat())
|
||||||
query.bindValue(":stop", self.sessionEnd.isoformat())
|
query.bindValue(":stop", self.sessionEnd.isoformat())
|
||||||
query.bindValue(":notes", self.textEdit.toPlainText())
|
query.bindValue(":notes", self.textEdit.toPlainText())
|
||||||
if not query.exec():
|
|
||||||
query_error(query)
|
|
||||||
session_id = query.lastInsertId()
|
|
||||||
model = self.wordView.model()
|
|
||||||
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 = (
|
|
||||||
"INSERT INTO session_block (session_id, section_id, block) VALUES "
|
|
||||||
)
|
|
||||||
parameters = ["(?,?,?)" for x in range(self.blocks.rowCount())]
|
|
||||||
sql += ",".join(parameters)
|
|
||||||
query.prepare(sql)
|
|
||||||
for row in range(self.blocks.rowCount()):
|
|
||||||
query.addBindValue(session_id)
|
|
||||||
item = self.blocks.item(row)
|
|
||||||
query.addBindValue(item.data(SessionDialog.SectionIdRole))
|
|
||||||
query.addBindValue(item.data(SessionDialog.BlockRole))
|
|
||||||
if not query.exec():
|
if not query.exec():
|
||||||
query_error(query)
|
query_error(query)
|
||||||
super().accept()
|
super().accept()
|
||||||
@@ -157,6 +131,19 @@ class SessionDialog(QDialog, Ui_Dialog):
|
|||||||
if state:
|
if state:
|
||||||
if not self.sessionStart:
|
if not self.sessionStart:
|
||||||
self.sessionStart = datetime.now()
|
self.sessionStart = datetime.now()
|
||||||
|
if self.session_id <= 0:
|
||||||
|
query = QSqlQuery()
|
||||||
|
query.prepare(
|
||||||
|
"INSERT INTO sessions "
|
||||||
|
"(person_id, start) "
|
||||||
|
"VALUES (:person_id, :start)"
|
||||||
|
)
|
||||||
|
query.bindValue(":person_id", self.person_id)
|
||||||
|
query.bindValue(":start", self.sessionStart.isoformat())
|
||||||
|
if not query.exec():
|
||||||
|
query_error(query)
|
||||||
|
self.session_id = query.lastInsertId()
|
||||||
|
|
||||||
self.startTime = datetime.now()
|
self.startTime = datetime.now()
|
||||||
self.timer.start()
|
self.timer.start()
|
||||||
else:
|
else:
|
||||||
@@ -197,13 +184,13 @@ class SessionDialog(QDialog, Ui_Dialog):
|
|||||||
raise Exception(f"Word_id({word_id}) not found in DB")
|
raise Exception(f"Word_id({word_id}) not found in DB")
|
||||||
word = QStandardItem()
|
word = QStandardItem()
|
||||||
word.setData(query.value("word"), Qt.ItemDataRole.DisplayRole)
|
word.setData(query.value("word"), Qt.ItemDataRole.DisplayRole)
|
||||||
word.setData(query.value("word_id"), SessionDialog.WordIdRole)
|
word.setData(word_id, SessionDialog.WordIdRole)
|
||||||
word.setData(0, SessionDialog.WordImportantRole)
|
word.setData(0, SessionDialog.WordImportantRole)
|
||||||
model = self.wordView.model()
|
model = self.wordView.model()
|
||||||
matches = model.match(
|
matches = model.match(
|
||||||
model.createIndex(0, 0),
|
model.createIndex(0, 0),
|
||||||
SessionDialog.WordIdRole,
|
SessionDialog.WordIdRole,
|
||||||
query.value("word_id"),
|
word_id,
|
||||||
1,
|
1,
|
||||||
Qt.MatchFlag.MatchExactly,
|
Qt.MatchFlag.MatchExactly,
|
||||||
)
|
)
|
||||||
@@ -211,6 +198,15 @@ class SessionDialog(QDialog, Ui_Dialog):
|
|||||||
return
|
return
|
||||||
self.wordView.model().appendRow(word)
|
self.wordView.model().appendRow(word)
|
||||||
self.wordView.model().sort(0)
|
self.wordView.model().sort(0)
|
||||||
|
query.prepare(
|
||||||
|
"INSERT INTO session_word "
|
||||||
|
"(session_id, word_id, important) "
|
||||||
|
"VALUES (:session_id, :word_id, 0)"
|
||||||
|
)
|
||||||
|
query.bindValue(":session_id", self.session_id)
|
||||||
|
query.bindValue(":word_id", word_id)
|
||||||
|
if not query.exec():
|
||||||
|
query_error(query)
|
||||||
else:
|
else:
|
||||||
print(f"Not active: {word_id}")
|
print(f"Not active: {word_id}")
|
||||||
return
|
return
|
||||||
@@ -255,4 +251,14 @@ class SessionDialog(QDialog, Ui_Dialog):
|
|||||||
)
|
)
|
||||||
self.textBrowser.textCursor().insertBlock()
|
self.textBrowser.textCursor().insertBlock()
|
||||||
self.textBrowser.ensureCursorVisible()
|
self.textBrowser.ensureCursorVisible()
|
||||||
|
query.prepare(
|
||||||
|
"INSERT INTO session_block "
|
||||||
|
"(session_id, section_id, block) "
|
||||||
|
"VALUES (:session_id, :section_id, :block)"
|
||||||
|
)
|
||||||
|
query.bindValue(":session_id", self.session_id)
|
||||||
|
query.bindValue(":section_id", section_id)
|
||||||
|
query.bindValue(":block", block)
|
||||||
|
if not query.exec():
|
||||||
|
query_error(query)
|
||||||
return
|
return
|
||||||
|
|||||||
108
lib/sounds.py
Normal file
108
lib/sounds.py
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
from PyQt6.QtCore import QObject, Qt, QUrl, pyqtSlot
|
||||||
|
from PyQt6.QtMultimedia import (
|
||||||
|
QAudioDevice,
|
||||||
|
QAudioOutput,
|
||||||
|
QMediaDevices,
|
||||||
|
QMediaPlayer,
|
||||||
|
QSoundEffect,
|
||||||
|
)
|
||||||
|
|
||||||
|
# from PyQt6.QtWidgets import QWidget
|
||||||
|
|
||||||
|
|
||||||
|
class SoundOff(QObject):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
#
|
||||||
|
# Setup devices
|
||||||
|
#
|
||||||
|
self.virtualDevice = None
|
||||||
|
dev = None
|
||||||
|
for output in QMediaDevices.audioOutputs():
|
||||||
|
if output.id().data().decode("utf-8") == "virt-input":
|
||||||
|
self.virtualDevice = output
|
||||||
|
if output.isDefault():
|
||||||
|
self.localDevice = output
|
||||||
|
|
||||||
|
self.alertEffect = QSoundEffect()
|
||||||
|
self.alertEffect.setSource(QUrl("qrc:/beep.wav"))
|
||||||
|
self.alertEffect.setAudioDevice(self.localDevice)
|
||||||
|
self.alertEffect.setVolume(0.25)
|
||||||
|
self.alertEffect.setLoopCount(1)
|
||||||
|
|
||||||
|
self.localPlayer = QMediaPlayer()
|
||||||
|
self.localPlayer.setObjectName("localPlayer")
|
||||||
|
self.localOutput = QAudioOutput()
|
||||||
|
self.localOutput.setDevice(self.localDevice)
|
||||||
|
self.localPlayer.setAudioOutput(self.localOutput)
|
||||||
|
if self.virtualDevice:
|
||||||
|
self.virtualPlayer = QMediaPlayer()
|
||||||
|
self.virtualPlayer.setObjectName("virtualPlayer")
|
||||||
|
self.virtualOutput = QAudioOutput()
|
||||||
|
self.virtualOutput.setDevice(self.virtualDevice)
|
||||||
|
self.virtualPlayer.setAudioOutput(self.virtualOutput)
|
||||||
|
#
|
||||||
|
# Connections
|
||||||
|
#
|
||||||
|
self.localPlayer.errorOccurred.connect(self.mediaError)
|
||||||
|
self.localPlayer.mediaStatusChanged.connect(self.mediaStatus)
|
||||||
|
self.localPlayer.playbackStateChanged.connect(self.playbackState)
|
||||||
|
if self.virtualDevice:
|
||||||
|
self.virtualPlayer.errorOccurred.connect(self.mediaError)
|
||||||
|
self.virtualPlayer.mediaStatusChanged.connect(self.mediaStatus)
|
||||||
|
self.virtualPlayer.playbackStateChanged.connect(self.playbackState)
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def alert(self):
|
||||||
|
self.alertEffect.play()
|
||||||
|
return
|
||||||
|
|
||||||
|
@pyqtSlot(QMediaPlayer.Error, str)
|
||||||
|
def mediaError(self, error, string):
|
||||||
|
print(error)
|
||||||
|
print(str)
|
||||||
|
return
|
||||||
|
|
||||||
|
@pyqtSlot(QMediaPlayer.MediaStatus)
|
||||||
|
def mediaStatus(self, status):
|
||||||
|
if status == QMediaPlayer.MediaStatus.LoadedMedia:
|
||||||
|
self.sender().play()
|
||||||
|
return
|
||||||
|
|
||||||
|
@pyqtSlot(QMediaPlayer.PlaybackState)
|
||||||
|
def playbackState(self, state):
|
||||||
|
return
|
||||||
|
|
||||||
|
#
|
||||||
|
# Communications slots
|
||||||
|
#
|
||||||
|
@pyqtSlot()
|
||||||
|
def soundAlert(self):
|
||||||
|
self.alertEffect.play()
|
||||||
|
return
|
||||||
|
|
||||||
|
@pyqtSlot(str)
|
||||||
|
def playSound(self, url):
|
||||||
|
src = QUrl(url)
|
||||||
|
if not self.localPlayer.audioOutput():
|
||||||
|
self.localPlayer.setAudioOutput(self.localOutput)
|
||||||
|
self.localPlayer.setSource(src)
|
||||||
|
self.localPlayer.setPosition(0)
|
||||||
|
if (
|
||||||
|
self.localPlayer.mediaStatus()
|
||||||
|
== QMediaPlayer.MediaStatus.LoadedMedia
|
||||||
|
):
|
||||||
|
self.localPlayer.play()
|
||||||
|
if not self.virtualDevice:
|
||||||
|
return
|
||||||
|
return
|
||||||
|
self.virtualPlayer.setSource(src)
|
||||||
|
self.virtualPlayer.setPosition(0)
|
||||||
|
if not self.virtualPlayer.audioOutput():
|
||||||
|
self.virtualPlayer.setAudioOutput(self.virtualOutput)
|
||||||
|
if (
|
||||||
|
self.virtualPlayer.mediaStatus()
|
||||||
|
== QMediaPlayer.MediaStatus.LoadedMedia
|
||||||
|
):
|
||||||
|
self.virtualPlayer.play()
|
||||||
|
return
|
||||||
32
main.py
32
main.py
@@ -1,15 +1,9 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
#
|
#
|
||||||
# TODO:
|
# TODO:
|
||||||
# Record all words examined
|
|
||||||
#
|
#
|
||||||
# Add definition to definition
|
# Add definition to definition
|
||||||
# Follow definition links
|
|
||||||
# Print subset of words, limit to words from this session's paragraphs
|
|
||||||
# plus defined during session
|
|
||||||
# Add Note per session
|
|
||||||
# Add book import dialog
|
# Add book import dialog
|
||||||
# Add person create/edit dialog
|
|
||||||
# Reading scroll with speed control
|
# Reading scroll with speed control
|
||||||
# Move controls out of reading window.
|
# Move controls out of reading window.
|
||||||
# Ability to edit text with updates to word-section links
|
# Ability to edit text with updates to word-section links
|
||||||
@@ -24,7 +18,14 @@ import sys
|
|||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from typing import cast
|
from typing import cast
|
||||||
|
|
||||||
from PyQt6.QtCore import QModelIndex, Qt, QTimer, pyqtSignal, pyqtSlot
|
from PyQt6.QtCore import (
|
||||||
|
QModelIndex,
|
||||||
|
QResource,
|
||||||
|
Qt,
|
||||||
|
QTimer,
|
||||||
|
pyqtSignal,
|
||||||
|
pyqtSlot,
|
||||||
|
)
|
||||||
from PyQt6.QtGui import (
|
from PyQt6.QtGui import (
|
||||||
QAction,
|
QAction,
|
||||||
QFont,
|
QFont,
|
||||||
@@ -59,10 +60,21 @@ def query_error(query: QSqlQuery) -> None:
|
|||||||
|
|
||||||
|
|
||||||
class MainWindow(QMainWindow, Ui_MainWindow):
|
class MainWindow(QMainWindow, Ui_MainWindow):
|
||||||
|
playAlert = pyqtSignal()
|
||||||
|
playSound = pyqtSignal(str)
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
super(MainWindow, self).__init__()
|
super(MainWindow, self).__init__()
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
# model = ModelOverride()
|
#
|
||||||
|
# Setup resources
|
||||||
|
#
|
||||||
|
if not QResource.registerResource(
|
||||||
|
os.path.join(os.path.dirname(__file__), "ui/resources.rcc"), "/"
|
||||||
|
):
|
||||||
|
raise Exception("Unable to register resources.rcc")
|
||||||
|
self.soundOff = SoundOff()
|
||||||
|
|
||||||
model = QSqlQueryModel()
|
model = QSqlQueryModel()
|
||||||
query = QSqlQuery("SELECT * FROM people ORDER BY name")
|
query = QSqlQuery("SELECT * FROM people ORDER BY name")
|
||||||
model.setQuery(query)
|
model.setQuery(query)
|
||||||
@@ -86,6 +98,8 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||||||
) # Y
|
) # Y
|
||||||
self.peopleView.doubleClicked.connect(self.editPerson) # Y
|
self.peopleView.doubleClicked.connect(self.editPerson) # Y
|
||||||
self.peopleView.clicked.connect(self.selectedPerson) # Y
|
self.peopleView.clicked.connect(self.selectedPerson) # Y
|
||||||
|
self.playAlert.connect(self.soundOff.alert)
|
||||||
|
self.playSound.connect(self.soundOff.playSound)
|
||||||
self.show()
|
self.show()
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -156,7 +170,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||||||
self.session.show()
|
self.session.show()
|
||||||
self.session.raise_()
|
self.session.raise_()
|
||||||
self.setPerson.emit(person_id)
|
self.setPerson.emit(person_id)
|
||||||
self.dlg = EditDialog(self.session, person_id)
|
self.dlg = EditDialog(self, self.session, person_id)
|
||||||
self.dlg.show()
|
self.dlg.show()
|
||||||
self.dlg.raise_()
|
self.dlg.raise_()
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
<file>print.css</file>
|
<file>print.css</file>
|
||||||
<file>display.css</file>
|
<file>display.css</file>
|
||||||
<file>email.css</file>
|
<file>email.css</file>
|
||||||
|
<file>beep.wav</file>
|
||||||
<file>opendyslexic/OpenDyslexic-Regular.otf</file>
|
<file>opendyslexic/OpenDyslexic-Regular.otf</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|||||||
BIN
ui/resources.rcc
BIN
ui/resources.rcc
Binary file not shown.
Reference in New Issue
Block a user