diff --git a/.gitignore b/.gitignore index 2d42b56..6566fec 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ venv *~ \#* +__pycache__ +*.db diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..db11f66 --- /dev/null +++ b/Makefile @@ -0,0 +1,4 @@ +main.py: ui/*.py + +%.py:%.ui + pyuic6 $< >$@ diff --git a/clean.sh b/clean.sh new file mode 100755 index 0000000..497ca83 --- /dev/null +++ b/clean.sh @@ -0,0 +1,5 @@ +#!/bin/bash +isort --profile black *.py lib/*.py +black *.py lib/*.py +flynt *.py lib/*.py + diff --git a/lib/__init__.py b/lib/__init__.py new file mode 100644 index 0000000..ba18408 --- /dev/null +++ b/lib/__init__.py @@ -0,0 +1,2 @@ +from .books import Book +from .read import EditDialog diff --git a/lib/books.py b/lib/books.py new file mode 100644 index 0000000..05319ca --- /dev/null +++ b/lib/books.py @@ -0,0 +1,67 @@ +import json +import os +import xml.dom.minidom + + +class Book: + sections = [] + metadata = {} + + def __init__(self, src: str) -> None: + super(Book, self).__init__() + self.parse_book(src) + return + + def parse_book(self, src: str) -> None: + with open(f"{src}/content.opf", "r") as f: + dom = xml.dom.minidom.parse(f) + metadata = dom.getElementsByTagName("metadata")[0] + for meta in metadata.childNodes: + if meta.nodeType != xml.dom.Node.ELEMENT_NODE: + continue + if meta.prefix == "dc": + self.metadata[meta.localName] = meta.firstChild.data + # + # The manifest contains a list of all the files contained in this + # EPUB + # + manifest = dom.getElementsByTagName("manifest")[0] + # + # The spine contains the documents in order they are read + # + spine = dom.getElementsByTagName("spine")[0] + for itemref in spine.childNodes: + if itemref.nodeType != xml.dom.Node.ELEMENT_NODE: + continue + # If linear == "no" skip + if ( + itemref.hasAttribute("linear") + and itemref.getAttribute("linear") == "no" + ): + continue + idref = itemref.getAttribute("idref") + for item in manifest.childNodes: + if item.nodeType != xml.dom.Node.ELEMENT_NODE: + continue + id = item.getAttribute("id") + if id == idref: + break + # + # A properly created *.opf will always have a correct + # spin/manifest. + # + href = item.getAttribute("href") + print(f"{idref}: {href}") + self.parse_section(src, href) + return + + def parse_section(self, src: str, href: str) -> None: + with open(f"{src}/{href}") as f: + dom = xml.dom.minidom.parse(f) + title = dom.getElementsByTagName("title")[0].firstChild.data + body = dom.getElementsByTagName("body")[0] + paragraphs = [] + for p in body.getElementsByTagName("p"): + paragraphs.append(p.toxml()) + self.sections.append({"title": title, "paragraphs": paragraphs}) + return diff --git a/lib/read.py b/lib/read.py new file mode 100644 index 0000000..fde328c --- /dev/null +++ b/lib/read.py @@ -0,0 +1,277 @@ +import json + +from PyDictionary import PyDictionary # type: ignore[import-untyped] +from PyQt6.QtCore import Qt, pyqtSlot +from PyQt6.QtGui import ( + QFont, + QTextCharFormat, + QTextCursor, + QTextDocument, + QTextListFormat, +) +from PyQt6.QtSql import QSqlDatabase, QSqlQuery, QSqlQueryModel +from PyQt6.QtWidgets import QDialog + +from main import query_error +from ui.EditDialog import Ui_Dialog + + +class EditDialog(QDialog, Ui_Dialog): + def __init__(self, book_id: int, person_id: int) -> None: + super(EditDialog, self).__init__() + self.book_id = book_id + self.person_id = person_id + self.setupUi(self) + self.current_paragraph(self.person_id) + self.paraEdit.setReadOnly(True) + self.defEdit.setReadOnly(True) + self.stackedWidget.setCurrentIndex(0) + self.defineBtn.clicked.connect(self.defineAction) + self.showBtn.clicked.connect(self.showAction) + self.nextBtn.clicked.connect(self.nextAction) + self.prevBtn.clicked.connect(self.prevAction) + return + + def current_paragraph(self, person_id: int) -> None: + query = QSqlQuery() + query.prepare("SELECT * FROM people WHERE person_id = :person_id") + query.bindValue(":person_id", person_id) + query.exec() + query.next() + paragraph_id = query.value("paragraph_id") + self.display_paragraph(paragraph_id) + return + + def display_paragraph(self, paragraph_id: int) -> None: + self.paragraph_id = paragraph_id + query = QSqlQuery() + query.prepare("SELECT * FROM paragraphs WHERE paragraph_id = :paragraph_id") + query.bindValue(":paragraph_id", paragraph_id) + query.exec() + query.next() + self.section_id = query.value("section_id") + self.paraSequence = query.value("sequence") + cursor = self.paraEdit.textCursor() + cursor.movePosition(QTextCursor.MoveOperation.Start) + cursor.movePosition( + QTextCursor.MoveOperation.End, QTextCursor.MoveMode.KeepAnchor + ) + cursor.removeSelectedText() + cursor.insertHtml(query.value("content")) + query.prepare( + "SELECT * FROM word_paragraph " "WHERE paragraph_id = :paragraph_id" + ) + query.bindValue(":paragraph_id", self.paragraph_id) + if not query.exec(): + query_error(query) + def_format = QTextCharFormat() + def_format.setFontUnderline(True) + cursor = QTextCursor(self.paraEdit.document()) + while query.next(): + start = query.value("start") + end = query.value("end") + cursor.setPosition(start, QTextCursor.MoveMode.MoveAnchor) + cursor.setPosition(end, QTextCursor.MoveMode.KeepAnchor) + cursor.setCharFormat(def_format) + cursor.setPosition(0) + self.paraEdit.setTextCursor(cursor) + return + + @pyqtSlot() + def defineAction(self) -> None: + cursor = self.paraEdit.textCursor() + word = cursor.selectedText() + start = cursor.selectionStart() + end = cursor.selectionEnd() + if start != end: + word = word.strip() + if len(word) == 0 or word.find(" ") >= 0: + return + if start > end: + tmp = start + start = end + end = tmp + + query = QSqlQuery() + query.prepare("SELECT * FROM words WHERE word = :word") + query.bindValue(":word", word) + if not query.exec(): + query_error(query) + if query.next(): # we have something + self.defined(query.value("word_id"), start, end) + return + dictionary = PyDictionary() + meaning = json.dumps(dictionary.meaning(word)) + por = dictionary.translate(word, "pt") + query.prepare( + "INSERT INTO words (word, definition, translation) " + "VALUES (:word, :definition, :translation)" + ) + query.bindValue(":word", word) + query.bindValue(":definition", meaning) + query.bindValue(":translation", por) + if not query.exec(): + query_error(query) + self.defined(query.lastInsertId(), start, end) + return + + def defined(self, word_id: int, start: int, end: int) -> None: + query = QSqlQuery() + query.prepare( + "SELECT * FROM word_paragraph " + "WHERE word_id = :word_id " + "AND paragraph_id = :paragraph_id " + "AND start = :start " + "AND end = :end" + ) + query.bindValue(":word_id", word_id) + query.bindValue(":paragraph_id", self.paragraph_id) + query.bindValue(":start", start) + query.bindValue(":end", end) + if not query.exec(): + query_error(query) + if query.next(): + return + query.prepare( + "INSERT INTO word_paragraph VALUES " + "( :word_id, :paragraph_id, :start, :end)" + ) + query.bindValue(":word_id", word_id) + query.bindValue(":paragraph_id", self.paragraph_id) + query.bindValue(":start", start) + query.bindValue(":end", end) + if not query.exec(): + query_error(query) + def_format = QTextCharFormat() + def_format.setFontUnderline(True) + cursor = QTextCursor(self.paraEdit.document()) + cursor.setPosition(start, QTextCursor.MoveMode.MoveAnchor) + cursor.setPosition(end, QTextCursor.MoveMode.KeepAnchor) + cursor.setCharFormat(def_format) + return + + def display_definition(self) -> None: + cursor = self.paraEdit.textCursor() + query = QSqlQuery() + query.prepare( + "SELECT w.* FROM word_paragraph wp " + "LEFT JOIN words w " + "ON (w.word_id = wp.word_id) " + "WHERE :position BETWEEN wp.start AND wp.end " + "AND wp.paragraph_id = :paragraph_id" + ) + query.bindValue(":position", cursor.position()) + query.bindValue(":paragraph_id", self.paragraph_id) + print("display_definition()", cursor.position()) + if not query.exec(): + query_error(query) + if not query.next(): + return + word = query.value("word") + definition = json.loads(query.value("definition")) + self.defEdit.document().clear() + cursor = self.defEdit.textCursor() + word_format = QTextCharFormat() + word_format.setFontPointSize(48) + word_format.setFontWeight(QFont.Weight.Bold) + cursor.insertText(word, word_format) + typeFormat = QTextListFormat() + typeFormat.setStyle(QTextListFormat.Style.ListDisc) + typeFormat.setIndent(1) + defFormat = QTextListFormat() + defFormat.setStyle(QTextListFormat.Style.ListCircle) + defFormat.setIndent(2) + word_format.setFontWeight(QFont.Weight.Normal) + word_format.setFontPointSize(16) + cursor.setCharFormat(word_format) + for key in definition.keys(): + cursor.insertList(typeFormat) + cursor.insertText(key) + cursor.insertList(defFormat) + first = True + for a_def in definition[key]: + if not first: + cursor.insertBlock() + cursor.insertText(a_def) + first = False + return + + @pyqtSlot() + def showAction(self) -> None: + idx = self.stackedWidget.currentIndex() + if idx == 0: + self.display_definition() + self.stackedWidget.setCurrentIndex(1 - idx) + return + + def nextDefinition(self): + cursor = self.paraEdit.textCursor() + position = cursor.position() + print(position) + formats = cursor.block().textFormats() + found = None + for f in formats: + wc = QTextCursor(cursor) + wc.setPosition(f.start) + wc.movePosition( + QTextCursor.MoveOperation.Right, + QTextCursor.MoveMode.KeepAnchor, + f.length, + ) + word = wc.selectedText() + cf = wc.charFormat() + if f.start <= position: + continue + if not cf.fontUnderline(): + continue + if not found: + found = f + elif f.start < found.start: + found = f + if found: + cursor.setPosition(found.start) + self.paraEdit.setTextCursor(cursor) + self.display_definition() + return + + @pyqtSlot() + def nextAction(self) -> None: + if self.stackedWidget.currentIndex() == 1: + self.nextDefinition() + return + paraQuery = QSqlQuery() + paraQuery.prepare( + "SELECT * FROM paragraphs WHERE " + "section_id=:section_id AND sequence = :sequence" + ) + paraQuery.bindValue(":section_id", self.section_id) + paraQuery.bindValue(":sequence", self.paraSequence + 1) + if not paraQuery.exec(): + query_error(paraQuery) + if not paraQuery.next(): + secQuery = QSqlQuery() + secQuery.prepare( + "SELECT * FROM sections WHERE book_id=:book_id " + "AND sequence = " + "(SELECT sequence FROM sections WHERE " + "section_id = :section_id)+1" + ) + secQuery.bindValue(":book_id", self.book_id) + secQuery.bindValue(":section_id", self.section_id) + if not secQuery.exec(): + query_error(secQuery) + if not secQuery.next(): + return + self.paraSequence = 0 + self.section_id = secQuery.value("section_id") + paraQuery.bindValue(":section_id", self.section_id) + paraQuery.bindValue(":sequence", self.paraSequence) + if not paraQuery.exec(): + query_error(paraQuery) + paraQuery.next() + self.display_paragraph(paraQuery.value("paragraph_id")) + return + + @pyqtSlot() + def prevAction(self) -> None: + return diff --git a/main.py b/main.py new file mode 100755 index 0000000..88b8d2b --- /dev/null +++ b/main.py @@ -0,0 +1,286 @@ +#!/usr/bin/env python3 +import os +import re +import sys + +from PyQt6.QtCore import Qt, pyqtSlot +from PyQt6.QtGui import ( + QFont, + QTextCharFormat, + QTextCursor, + QTextDocument, + QTextListFormat, +) +from PyQt6.QtSql import QSqlDatabase, QSqlQuery, QSqlQueryModel +from PyQt6.QtWidgets import QApplication, QFileDialog, QMainWindow + +from lib import * +from ui.MainWindow import Ui_MainWindow + +# from ui.testWindow import Ui_MainWindow + + +def query_error(query: QSqlQuery) -> None: + print( + "SQL Error:\n{}\n{}\n{}:{}".format( + query.executedQuery(), + query.boundValues(), + query.lastError().type(), + query.lastError().text(), + ) + ) + raise Exception("SQL Error") + + +class MainWindow(QMainWindow, Ui_MainWindow): + book_id = 0 + + def __init__(self) -> None: + super(MainWindow, self).__init__() + self.setupUi(self) + model = QSqlQueryModel() + query = QSqlQuery("SELECT * FROM people ORDER BY name") + model.setQuery(query) + self.peopleView.setModel(model) + self.peopleView.setModelColumn(1) + # self.load_definition(word, PyDictionary.meaning(word)) + # self.wordButton.clicked.connect(self.wordAction) + self.ReadButton.clicked.connect(self.readAction) + self.bookBtn.clicked.connect(self.bookAction) + self.show() + return + + @pyqtSlot() + def bookAction(self) -> None: + directory = QFileDialog.getExistingDirectory() + book = Book(directory) + self.book_id = self.store_book(book) + return + + def store_book(self, book: Book) -> int: + uuid = book.metadata["identifier"] + query = QSqlQuery() + query.prepare( + "SELECT COUNT(*) AS number, book_id FROM books b " "WHERE b.uuid = :uuid" + ) + query.bindValue(":uuid", uuid) + if not query.exec(): + query_error(query) + query.next() + if query.value("number") > 0: + book_id: int = query.value("book_id") + return book_id + query.prepare( + "INSERT INTO books (title, author, uuid, level) VALUES (" + ":title, :author, :uuid, 0)" + ) + query.bindValue(":title", book.metadata["title"]) + query.bindValue(":author", book.metadata["creator"]) + query.bindValue(":uuid", uuid) + if not query.exec(): + query_error(query) + book_id = query.lastInsertId() + section_query = QSqlQuery() + section_query.prepare( + "INSERT INTO sections (title, sequence, book_id) " + "VALUES (:title, :sequence, :book_id)" + ) + section_query.bindValue(":book_id", book_id) + para_query = QSqlQuery() + para_query.prepare( + "INSERT INTO paragraphs (section_id, sequence, content) " + "VALUES (:section_id, :sequence, :content)" + ) + for seq, section in enumerate(book.sections): + section_query.bindValue(":title", section["title"]) + section_query.bindValue(":sequence", seq) + if not section_query.exec(): + query_error(section_query) + section_id = query.lastInsertId() + para_query.bindValue(":section_id", section_id) + for ps, paragraph in enumerate(section["paragraphs"]): + para_query.bindValue(":sequence", ps) + para_query.bindValue(":content", paragraph) + if not para_query.exec(): + query_error(para_query) + return book_id + + def load_definition(self, word: str, definition: dict) -> None: + document = None # self.textEdit.document() + myCursor = QTextCursor(document) + myCursor.movePosition(QTextCursor.MoveOperation.Start) + myCursor.movePosition( + QTextCursor.MoveOperation.End, QTextCursor.MoveMode.KeepAnchor + ) + myCursor.removeSelectedText() + word_format = QTextCharFormat() + # word_format.setFontFamily("Caveat") + word_format.setFontPointSize(48) + word_format.setFontWeight(QFont.Weight.Bold) + myCursor.insertText(word, word_format) + # word_format.setFont(document.defaultFont()) + typeFormat = QTextListFormat() + typeFormat.setStyle(QTextListFormat.Style.ListDisc) + typeFormat.setIndent(1) + defFormat = QTextListFormat() + defFormat.setStyle(QTextListFormat.Style.ListCircle) + defFormat.setIndent(2) + myCursor.setCharFormat(word_format) + for key in definition.keys(): + myCursor.insertList(typeFormat) + myCursor.insertText(key) + myCursor.insertList(defFormat) + first = True + for a_def in definition[key]: + if not first: + myCursor.insertBlock() + myCursor.insertText(a_def) + first = False + return + + @pyqtSlot() + def readAction(self) -> None: + indexes = self.peopleView.selectedIndexes() + if len(indexes) < 1: + return + person_id = indexes[0].siblingAtColumn(0).data() + name = indexes[0].data() + print(person_id, name) + dlg = EditDialog(self.book_id, person_id) + dlg.exec() + return + + +SQL_CMDS = [ + "PRAGMA foreign_keys=ON", + "CREATE TABLE IF NOT EXISTS words " + "(word_id INTEGER PRIMARY KEY AUTOINCREMENT, word TEXT, definition TEXT, " + "translation TEXT)", + "CREATE TABLE IF NOT EXISTS books " + "(book_id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT, author TEXT, " + "uuid TEXT, level INTEGER)", + "CREATE TABLE IF NOT EXISTS sections " + "(section_id INTEGER PRIMARY KEY AUTOINCREMENT, " + "title TEXT, sequence INTEGER, " + "book_id INTEGER REFERENCES books ON DELETE CASCADE) ", + "CREATE TABLE IF NOT EXISTS paragraphs " + "(paragraph_id INTEGER PRIMARY KEY AUTOINCREMENT, " + "section_id INTEGER REFERENCES sections ON DELETE CASCADE, " + "sequence INTEGER NOT NULL DEFAULT 0, " + "content TEXT)", + "CREATE TABLE IF NOT EXISTS people " + "(person_id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, " + "organization TEXT, " + "paragraph_id INTEGER REFERENCES paragraphs ON DELETE CASCADE)", + "CREATE TABLE IF NOT EXISTS person_book " + "(person_id INTEGER REFERENCES people ON DELETE CASCADE, " + "book_id INTEGER REFERENCES books ON DELETE CASCADE)", + "CREATE TABLE IF NOT EXISTS word_paragraph " + "(word_id INTEGER REFERENCES words ON DELETE CASCADE, " + "paragraph_id INTEGER REFERENCES paragraphs ON DELETE CASCADE, " + "start INTEGER NOT NULL, end INTEGER NOT NULL)", +] + + +def schema_update(db: QSqlDatabase) -> None: + query = QSqlQuery() + + for sql in SQL_CMDS: + print(sql) + inlower = sql.lower() + if not inlower.startswith("create table "): + if not query.exec(sql): + query_error(query) + continue + create_cmd = re.sub(r"IF NOT EXISTS ", "", sql) + create_cmd = re.sub(r"\s\s*", " ", create_cmd) + matches = re.search(r"^(CREATE TABLE )([^ ]+)( \(.+)$", create_cmd) + if matches: + table_name = matches.group(2) + create_cmd = ( + matches.group(1) + '"' + matches.group(2) + '"' + matches.group(3) + ) + else: + raise AttributeError(f"No match found: {create_cmd}") + + query.prepare("SELECT sql FROM sqlite_schema WHERE tbl_name = :tbl") + query.bindValue(":tbl", table_name) + if not query.exec(): + query_error(query) + query.next() + old = query.value(0) + if not old: + if not query.exec(sql): + query_error(query) + continue + if old.lower() == create_cmd.lower(): + continue + print(old.lower()) + print(create_cmd.lower()) + print(f"Updating: {table_name}") + + # Step 1 turn off foreign key constraints + if not query.exec("PRAGMA foreign_keys=OFF"): + query_error(query) + # Step 2 start a transaction + db.transaction() + # Step 3 remember old indexes, triggers, and views + # Step 4 create new table + new_table_name = table_name + "_new" + if not query.exec(matches.group(1) + new_table_name + matches.group(3)): + query_error(query) + # step 5 transfer content + coldefs = re.search(r"\((.+)\)", old).group(1).split(", ") + cols = [x.split(" ")[0] for x in coldefs] + if not query.exec( + "INSERT INTO " + + new_table_name + + "(" + + ", ".join(cols) + + ") SELECT " + + ", ".join(cols) + + " FROM " + + table_name + ): + query_error(query) + + # step 6 Drop old table + if not query.exec("DROP TABLE " + table_name): + query_error(query) + # step 6 rename new table to old table + if not query.exec("ALTER TABLE " + new_table_name + " RENAME TO " + table_name): + query_error(query) + + # step 8 create indexes, triggers, and views + # step 9 rebuild affected views + # step 10 turn foreign key constrants back on + if not query.exec("PRAGMA foreign_keys=ON"): + query_error(query) + # step 11 commit the changes + db.commit() + return + + +def main() -> int: + db = QSqlDatabase() + db = db.addDatabase("QSQLITE") + db.setDatabaseName("twel.db") + db.open() + app = QApplication(sys.argv) + schema_update(db) + window: QMainWindow = MainWindow() + return app.exec() + + +if __name__ == "__main__": + outOfDate = [] + for fileName in os.listdir("ui"): + if not fileName.endswith(".py"): + continue + uiName = fileName[:-3] + ".ui" + if os.path.getmtime("ui/" + uiName) > os.path.getmtime("ui/" + fileName): + outOfDate.append(fileName) + if len(outOfDate) > 0: + print(f"UI out of date: {', '.join(outOfDate)}") + sys.exit(1) + sys.exit(main()) diff --git a/mypy.ini b/mypy.ini new file mode 100644 index 0000000..c7ebda8 --- /dev/null +++ b/mypy.ini @@ -0,0 +1,8 @@ +[mypy] +warn_redundant_casts = True +disallow_untyped_defs = True +warn_return_any = True +warn_unused_configs = True + +[mypy-ui.*] +disallow_untyped_defs = False diff --git a/ui/EditDialog.py b/ui/EditDialog.py new file mode 100644 index 0000000..15f008b --- /dev/null +++ b/ui/EditDialog.py @@ -0,0 +1,78 @@ +# Form implementation generated from reading ui file 'ui/EditDialog.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(761, 427) + self.horizontalLayout = QtWidgets.QHBoxLayout(Dialog) + self.horizontalLayout.setObjectName("horizontalLayout") + self.stackedWidget = QtWidgets.QStackedWidget(parent=Dialog) + self.stackedWidget.setObjectName("stackedWidget") + self.page = QtWidgets.QWidget() + self.page.setObjectName("page") + self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.page) + self.verticalLayout_2.setObjectName("verticalLayout_2") + self.paraEdit = QtWidgets.QTextEdit(parent=self.page) + font = QtGui.QFont() + font.setFamily("OpenDyslexic") + font.setPointSize(16) + self.paraEdit.setFont(font) + self.paraEdit.setObjectName("paraEdit") + self.verticalLayout_2.addWidget(self.paraEdit) + self.stackedWidget.addWidget(self.page) + self.page_2 = QtWidgets.QWidget() + self.page_2.setObjectName("page_2") + self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.page_2) + self.verticalLayout_3.setObjectName("verticalLayout_3") + self.defEdit = QtWidgets.QTextEdit(parent=self.page_2) + font = QtGui.QFont() + font.setFamily("OpenDyslexic") + self.defEdit.setFont(font) + self.defEdit.setObjectName("defEdit") + self.verticalLayout_3.addWidget(self.defEdit) + self.stackedWidget.addWidget(self.page_2) + self.horizontalLayout.addWidget(self.stackedWidget) + self.widget = QtWidgets.QWidget(parent=Dialog) + self.widget.setObjectName("widget") + self.verticalLayout = QtWidgets.QVBoxLayout(self.widget) + self.verticalLayout.setObjectName("verticalLayout") + self.defineBtn = QtWidgets.QPushButton(parent=self.widget) + self.defineBtn.setObjectName("defineBtn") + self.verticalLayout.addWidget(self.defineBtn) + self.wordsBtn = QtWidgets.QPushButton(parent=self.widget) + self.wordsBtn.setObjectName("wordsBtn") + self.verticalLayout.addWidget(self.wordsBtn) + self.showBtn = QtWidgets.QPushButton(parent=self.widget) + self.showBtn.setObjectName("showBtn") + self.verticalLayout.addWidget(self.showBtn) + spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding) + self.verticalLayout.addItem(spacerItem) + self.nextBtn = QtWidgets.QPushButton(parent=self.widget) + self.nextBtn.setObjectName("nextBtn") + self.verticalLayout.addWidget(self.nextBtn) + self.prevBtn = QtWidgets.QPushButton(parent=self.widget) + self.prevBtn.setObjectName("prevBtn") + self.verticalLayout.addWidget(self.prevBtn) + self.horizontalLayout.addWidget(self.widget) + + self.retranslateUi(Dialog) + self.stackedWidget.setCurrentIndex(0) + QtCore.QMetaObject.connectSlotsByName(Dialog) + + def retranslateUi(self, Dialog): + _translate = QtCore.QCoreApplication.translate + Dialog.setWindowTitle(_translate("Dialog", "Dialog")) + self.defineBtn.setText(_translate("Dialog", "Define")) + self.wordsBtn.setText(_translate("Dialog", "Words")) + self.showBtn.setText(_translate("Dialog", "Show")) + self.nextBtn.setText(_translate("Dialog", "Next")) + self.prevBtn.setText(_translate("Dialog", "Previous")) diff --git a/ui/EditDialog.ui b/ui/EditDialog.ui new file mode 100644 index 0000000..e6b68cf --- /dev/null +++ b/ui/EditDialog.ui @@ -0,0 +1,109 @@ + + + Dialog + + + + 0 + 0 + 761 + 427 + + + + Dialog + + + + + + 0 + + + + + + + + OpenDyslexic + 16 + + + + + + + + + + + + + OpenDyslexic + + + + + + + + + + + + + + + Define + + + + + + + Words + + + + + + + Show + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Next + + + + + + + Previous + + + + + + + + + + + diff --git a/ui/MainWindow.py b/ui/MainWindow.py new file mode 100644 index 0000000..65633e4 --- /dev/null +++ b/ui/MainWindow.py @@ -0,0 +1,56 @@ +# Form implementation generated from reading ui file 'ui/MainWindow.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_MainWindow(object): + def setupUi(self, MainWindow): + MainWindow.setObjectName("MainWindow") + MainWindow.resize(800, 600) + self.centralwidget = QtWidgets.QWidget(parent=MainWindow) + self.centralwidget.setObjectName("centralwidget") + self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget) + self.horizontalLayout.setObjectName("horizontalLayout") + self.peopleView = QtWidgets.QListView(parent=self.centralwidget) + self.peopleView.setObjectName("peopleView") + self.horizontalLayout.addWidget(self.peopleView) + self.widget = QtWidgets.QWidget(parent=self.centralwidget) + self.widget.setObjectName("widget") + self.verticalLayout = QtWidgets.QVBoxLayout(self.widget) + self.verticalLayout.setObjectName("verticalLayout") + self.WordButton = QtWidgets.QPushButton(parent=self.widget) + self.WordButton.setObjectName("WordButton") + self.verticalLayout.addWidget(self.WordButton) + self.ReadButton = QtWidgets.QPushButton(parent=self.widget) + self.ReadButton.setObjectName("ReadButton") + self.verticalLayout.addWidget(self.ReadButton) + spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding) + self.verticalLayout.addItem(spacerItem) + self.bookBtn = QtWidgets.QPushButton(parent=self.widget) + self.bookBtn.setObjectName("bookBtn") + self.verticalLayout.addWidget(self.bookBtn) + self.horizontalLayout.addWidget(self.widget) + MainWindow.setCentralWidget(self.centralwidget) + self.menubar = QtWidgets.QMenuBar(parent=MainWindow) + self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 22)) + self.menubar.setObjectName("menubar") + MainWindow.setMenuBar(self.menubar) + self.statusbar = QtWidgets.QStatusBar(parent=MainWindow) + self.statusbar.setObjectName("statusbar") + MainWindow.setStatusBar(self.statusbar) + + self.retranslateUi(MainWindow) + QtCore.QMetaObject.connectSlotsByName(MainWindow) + + def retranslateUi(self, MainWindow): + _translate = QtCore.QCoreApplication.translate + MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow")) + self.WordButton.setText(_translate("MainWindow", "Words")) + self.ReadButton.setText(_translate("MainWindow", "Read")) + self.bookBtn.setText(_translate("MainWindow", "Add Book")) diff --git a/ui/MainWindow.ui b/ui/MainWindow.ui new file mode 100644 index 0000000..8335de1 --- /dev/null +++ b/ui/MainWindow.ui @@ -0,0 +1,77 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + MainWindow + + + + + + + + + + + + + Words + + + + + + + Read + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Add Book + + + + + + + + + + + + 0 + 0 + 800 + 22 + + + + + + + + diff --git a/ui/WordDialog.py b/ui/WordDialog.py new file mode 100644 index 0000000..09058d8 --- /dev/null +++ b/ui/WordDialog.py @@ -0,0 +1,42 @@ +# Form implementation generated from reading ui file 'ui/WordDialog.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(802, 300) + self.verticalLayout = QtWidgets.QVBoxLayout(Dialog) + self.verticalLayout.setObjectName("verticalLayout") + self.horizontalLayout = QtWidgets.QHBoxLayout() + self.horizontalLayout.setObjectName("horizontalLayout") + self.wordList = QtWidgets.QListView(parent=Dialog) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.MinimumExpanding, QtWidgets.QSizePolicy.Policy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.wordList.sizePolicy().hasHeightForWidth()) + self.wordList.setSizePolicy(sizePolicy) + self.wordList.setObjectName("wordList") + self.horizontalLayout.addWidget(self.wordList) + self.definition = QtWidgets.QTextEdit(parent=Dialog) + self.definition.setObjectName("definition") + self.horizontalLayout.addWidget(self.definition) + self.verticalLayout.addLayout(self.horizontalLayout) + self.buttonBox = QtWidgets.QDialogButtonBox(parent=Dialog) + self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.StandardButton.Ok) + self.buttonBox.setObjectName("buttonBox") + self.verticalLayout.addWidget(self.buttonBox) + + self.retranslateUi(Dialog) + QtCore.QMetaObject.connectSlotsByName(Dialog) + + def retranslateUi(self, Dialog): + _translate = QtCore.QCoreApplication.translate + Dialog.setWindowTitle(_translate("Dialog", "Dialog")) diff --git a/ui/WordDialog.ui b/ui/WordDialog.ui new file mode 100644 index 0000000..88da08f --- /dev/null +++ b/ui/WordDialog.ui @@ -0,0 +1,45 @@ + + + Dialog + + + + 0 + 0 + 802 + 300 + + + + Dialog + + + + + + + + + 0 + 0 + + + + + + + + + + + + + QDialogButtonBox::Ok + + + + + + + + diff --git a/ui/testWindow.py b/ui/testWindow.py new file mode 100644 index 0000000..2265e10 --- /dev/null +++ b/ui/testWindow.py @@ -0,0 +1,37 @@ +# Form implementation generated from reading ui file 'ui/testWindow.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_MainWindow(object): + def setupUi(self, MainWindow): + MainWindow.setObjectName("MainWindow") + MainWindow.resize(800, 600) + self.centralwidget = QtWidgets.QWidget(parent=MainWindow) + self.centralwidget.setObjectName("centralwidget") + self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget) + self.verticalLayout.setObjectName("verticalLayout") + self.textEdit = QtWidgets.QTextEdit(parent=self.centralwidget) + self.textEdit.setObjectName("textEdit") + self.verticalLayout.addWidget(self.textEdit) + MainWindow.setCentralWidget(self.centralwidget) + self.menubar = QtWidgets.QMenuBar(parent=MainWindow) + self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 19)) + self.menubar.setObjectName("menubar") + MainWindow.setMenuBar(self.menubar) + self.statusbar = QtWidgets.QStatusBar(parent=MainWindow) + self.statusbar.setObjectName("statusbar") + MainWindow.setStatusBar(self.statusbar) + + self.retranslateUi(MainWindow) + QtCore.QMetaObject.connectSlotsByName(MainWindow) + + def retranslateUi(self, MainWindow): + _translate = QtCore.QCoreApplication.translate + MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow")) diff --git a/ui/testWindow.ui b/ui/testWindow.ui new file mode 100644 index 0000000..005f94b --- /dev/null +++ b/ui/testWindow.ui @@ -0,0 +1,37 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + MainWindow + + + + + + + + + + + + 0 + 0 + 800 + 19 + + + + + + + +