Compare commits
No commits in common. "8db7eae1e411dc4e0bd3b266ddf3a56318660121" and "d242e4ff6860ebc05666157b5f6d17deebada8b1" have entirely different histories.
8db7eae1e4
...
d242e4ff68
@ -1,4 +0,0 @@
|
||||
DB_USER=
|
||||
DB_NAME=
|
||||
DB_PASSWORD=
|
||||
DB_HOST=
|
15
.vscode/launch.json
vendored
15
.vscode/launch.json
vendored
@ -1,15 +0,0 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Python Debugger: Current File",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/src/app.py",
|
||||
"console": "integratedTerminal"
|
||||
}
|
||||
]
|
||||
}
|
@ -36,10 +36,4 @@ My solution will use the following tech stack:
|
||||
4. **SqlAlchemy** for database management
|
||||
5. **`python-dotenv`** for configurability
|
||||
|
||||
The problem in question will be demonstrated on a library application
|
||||
|
||||
## Sources
|
||||
1. [Qt documentation for pyside2](https://doc.qt.io/qtforpython-5/PySide2/QtWidgets)
|
||||
1. [Qt documentation for PySide6](https://doc.qt.io/qtforpython-6/PySide6/QtWidgets/)
|
||||
2. [Pythontutorial.net](https://www.pythontutorial.net/pyqt/pyqt-qtoolbar/)
|
||||
3. [Docs.sqlalchemy.org](https://docs.sqlalchemy.org/)
|
||||
The problem in question will be demonstrated on a library application
|
@ -1,13 +1,11 @@
|
||||
greenlet==3.1.1
|
||||
mypy==1.14.1
|
||||
mypy-extensions==1.0.0
|
||||
mysql-connector-python==9.1.0
|
||||
PySide6==6.8.1
|
||||
PySide6_Addons==6.8.1
|
||||
PySide6_Essentials==6.8.1
|
||||
python-dotenv==1.0.1
|
||||
shiboken2==5.13.2
|
||||
shiboken6==6.8.1
|
||||
SQLAlchemy==2.0.36
|
||||
sqlalchemy-stubs==0.4
|
||||
typing_extensions==4.12.2
|
||||
|
69
src/app.py
69
src/app.py
@ -1,39 +1,56 @@
|
||||
import sys
|
||||
from PySide6 import QtWidgets, QtCore
|
||||
# import sys
|
||||
# from PySide6.QtGui import QGuiApplication
|
||||
# from PySide6.QtQml import QQmlApplicationEngine
|
||||
|
||||
# if __name__ == "__main__":
|
||||
# app = QGuiApplication(sys.argv)
|
||||
# engine = QQmlApplicationEngine()
|
||||
# engine.addImportPath(sys.path[0])
|
||||
# engine.loadFromModule("ui", "Main")
|
||||
# if not engine.rootObjects():
|
||||
# sys.exit(-1)
|
||||
# exit_code = app.exec()
|
||||
# del engine
|
||||
# sys.exit(exit_code)
|
||||
|
||||
|
||||
from ui.dashboard import LibraryDashboard
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
from models.book import Book, BookStatusEnum
|
||||
from models.author import Author
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = QtWidgets.QApplication([])
|
||||
window = LibraryDashboard()
|
||||
window.show()
|
||||
sys.exit(app.exec())
|
||||
# Replace with your MySQL database credentials
|
||||
DATABASE_URI = 'mysql+mysqlconnector://username:password@localhost:3306/library'
|
||||
|
||||
# from sqlalchemy import create_engine
|
||||
# from sqlalchemy.orm import sessionmaker
|
||||
# Create the engine
|
||||
engine = create_engine(DATABASE_URI, echo=True)
|
||||
|
||||
# from models.book import Book, BookStatusEnum
|
||||
# from models.author import Author
|
||||
# Create a configured session class
|
||||
SessionLocal = sessionmaker(bind=engine)
|
||||
|
||||
# # Replace with your MySQL database credentials
|
||||
# DATABASE_URI = 'mysql+mysqlconnector://username:password@localhost:3306/library'
|
||||
# Create a session instance
|
||||
session = SessionLocal()
|
||||
|
||||
# # Create the engine
|
||||
# engine = create_engine(DATABASE_URI)
|
||||
new_author = Author(first_name="John", last_name="Doe")
|
||||
|
||||
# from models.base import Base
|
||||
# Base.metadata.create_all(engine)
|
||||
session.add(new_author)
|
||||
session.commit()
|
||||
|
||||
# # Create a configured session class
|
||||
# SessionLocal = sessionmaker(bind=engine)
|
||||
new_book = Book(
|
||||
title="Sample Book",
|
||||
description="A fascinating tale.",
|
||||
year_published="2023",
|
||||
isbn="1234567890123",
|
||||
status='available',
|
||||
author_id=new_author.id
|
||||
)
|
||||
|
||||
# # Create a session instance
|
||||
# session = SessionLocal()
|
||||
session.add(new_book)
|
||||
session.commit()
|
||||
|
||||
# books = session.query(Book).all()
|
||||
# for book in books:
|
||||
# print(book.title, book.author.first_name)
|
||||
books = session.query(Book).all()
|
||||
for book in books:
|
||||
print(book.title, book.author.first_name)
|
||||
|
||||
# session.close()
|
||||
session.close()
|
@ -1,10 +1,7 @@
|
||||
from .base import Base
|
||||
from .author import Author
|
||||
from .book import Book
|
||||
from .book_category import BookCategory
|
||||
from .book import Book
|
||||
from .book_category_link import BookCategoryLink
|
||||
from .member import Member
|
||||
from .librarian import Librarian
|
||||
from .loan import Loan
|
||||
|
||||
__all__ = ["Author", "Book", "BookCategory", "BookCategoryLink", "Member", "Librarian", "Loan"]
|
||||
from .loan import Loan
|
@ -1,17 +1,15 @@
|
||||
from sqlalchemy import Column, Integer, String, TIMESTAMP, UniqueConstraint, func
|
||||
from sqlalchemy.orm import relationship
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
|
||||
from .base import Base
|
||||
Base = declarative_base()
|
||||
|
||||
|
||||
class Author(Base):
|
||||
__tablename__ = 'author'
|
||||
__tablename__ = 'author'
|
||||
__table_args__ = (UniqueConstraint('id'),)
|
||||
|
||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||
first_name = Column(String(50), nullable=False)
|
||||
last_name = Column(String(50), nullable=False)
|
||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||
first_name = Column(String(50), nullable=False)
|
||||
last_name = Column(String(50), nullable=False)
|
||||
last_updated = Column(TIMESTAMP, nullable=False, server_default=func.now())
|
||||
|
||||
# Reference 'Book' as a string to avoid direct import
|
||||
books = relationship('Book', back_populates='author')
|
||||
|
@ -1,3 +0,0 @@
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
|
||||
Base = declarative_base()
|
@ -2,8 +2,12 @@ import enum
|
||||
|
||||
from sqlalchemy import Column, Integer, String, TIMESTAMP, Text, ForeignKey, Enum, UniqueConstraint, func
|
||||
from sqlalchemy.orm import relationship
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
|
||||
from .base import Base
|
||||
from .author import Author
|
||||
from .book_category import BookCategory
|
||||
|
||||
Base = declarative_base()
|
||||
|
||||
|
||||
class BookStatusEnum(enum.Enum):
|
||||
@ -11,20 +15,18 @@ class BookStatusEnum(enum.Enum):
|
||||
borrowed = 'borrowed'
|
||||
reserved = 'reserved'
|
||||
|
||||
|
||||
class Book(Base):
|
||||
__tablename__ = 'book'
|
||||
__tablename__ = 'book'
|
||||
__table_args__ = (UniqueConstraint('isbn'),)
|
||||
|
||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||
author_id = Column(Integer, ForeignKey('author.id'), nullable=False)
|
||||
title = Column(String(100), nullable=False)
|
||||
description = Column(Text, nullable=False)
|
||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||
author_id = Column(Integer, ForeignKey('author.id'), nullable=False)
|
||||
title = Column(String(100), nullable=False)
|
||||
description = Column(Text, nullable=False)
|
||||
year_published = Column(String(4), nullable=False)
|
||||
isbn = Column(String(13), nullable=False, unique=True)
|
||||
status = Column(Enum(BookStatusEnum), nullable=False, default=BookStatusEnum.available)
|
||||
created_at = Column(TIMESTAMP, nullable=False, server_default=func.now())
|
||||
last_updated = Column(TIMESTAMP, nullable=False, server_default=func.now())
|
||||
isbn = Column(String(13), nullable=False, unique=True)
|
||||
status = Column(Enum(BookStatusEnum), nullable=False, default=BookStatusEnum.available)
|
||||
created_at = Column(TIMESTAMP, nullable=False, server_default=func.now())
|
||||
last_updated = Column(TIMESTAMP, nullable=False, server_default=func.now())
|
||||
|
||||
# Reference 'Author' as a string to avoid direct import
|
||||
author = relationship('Author', back_populates='books')
|
||||
author = relationship('Author', backref='books')
|
||||
|
@ -1,7 +1,9 @@
|
||||
from sqlalchemy import Column, Integer, String, TIMESTAMP, ForeignKey, UniqueConstraint, func
|
||||
from sqlalchemy.orm import relationship
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
|
||||
Base = declarative_base()
|
||||
|
||||
from .base import Base
|
||||
|
||||
class BookCategory(Base):
|
||||
__tablename__ = 'book_category'
|
||||
|
@ -1,9 +1,12 @@
|
||||
from sqlalchemy import Column, Integer, ForeignKey
|
||||
from sqlalchemy.orm import relationship
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
|
||||
|
||||
from .book import Book
|
||||
from .book_category import BookCategory
|
||||
from .base import Base
|
||||
|
||||
Base = declarative_base()
|
||||
|
||||
class BookCategoryLink(Base):
|
||||
__tablename__ = 'book_category_link'
|
||||
|
@ -2,8 +2,10 @@ import enum
|
||||
|
||||
from sqlalchemy import Column, Integer, String, TIMESTAMP, Text, ForeignKey, Enum, UniqueConstraint, func
|
||||
from sqlalchemy.orm import relationship
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
|
||||
from .base import Base
|
||||
|
||||
Base = declarative_base()
|
||||
|
||||
|
||||
class LibrarianStatusEnum(enum.Enum):
|
||||
|
@ -2,12 +2,14 @@ import enum
|
||||
|
||||
from sqlalchemy import Column, Integer, String, TIMESTAMP, Text, ForeignKey, Enum, UniqueConstraint, Float, func
|
||||
from sqlalchemy.orm import relationship
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
|
||||
from .base import Base
|
||||
from .book import Book
|
||||
from .member import Member
|
||||
from .librarian import Librarian
|
||||
|
||||
Base = declarative_base()
|
||||
|
||||
|
||||
class LoanStatusEnum(enum.Enum):
|
||||
borrowed = 'borrowed'
|
||||
|
@ -2,14 +2,13 @@ import enum
|
||||
|
||||
from sqlalchemy import Column, Integer, String, TIMESTAMP, Text, ForeignKey, Enum, UniqueConstraint, func
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from .base import Base
|
||||
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
|
||||
class MemberStatusEnum(enum.Enum):
|
||||
active = 'active'
|
||||
inactive = 'inactive'
|
||||
|
||||
Base = declarative_base()
|
||||
|
||||
class Member(Base):
|
||||
__tablename__ = 'member'
|
||||
|
32
src/ui/Main.qml
Normal file
32
src/ui/Main.qml
Normal file
@ -0,0 +1,32 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
Window {
|
||||
width: 300
|
||||
height: 200
|
||||
visible: true
|
||||
title: "Hello World"
|
||||
|
||||
readonly property list<string> texts: ["Hallo Welt", "Hei maailma", "Hola Mundo", "Привет мир"]
|
||||
|
||||
function setText() {
|
||||
var i = Math.round(Math.random() * 3);
|
||||
text.text = texts[i];
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
|
||||
Text {
|
||||
id: text
|
||||
text: "Hello World"
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
Button {
|
||||
text: "Click me"
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
onClicked: setText()
|
||||
}
|
||||
}
|
||||
}
|
@ -1,125 +0,0 @@
|
||||
from PySide6.QtGui import QGuiApplication, QAction
|
||||
from PySide6.QtQml import QQmlApplicationEngine
|
||||
from PySide6 import QtWidgets, QtCore
|
||||
|
||||
from ui.settings import SettingsDialog
|
||||
|
||||
class LibraryDashboard(QtWidgets.QMainWindow):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
# Set up main window properties
|
||||
self.setWindowTitle("Library Dashboard")
|
||||
self.setGeometry(100, 100, 400, 400)
|
||||
|
||||
# Set up menu bar
|
||||
self.create_menu_bar()
|
||||
|
||||
# Central widget and layout
|
||||
central_widget = QtWidgets.QWidget()
|
||||
self.setCentralWidget(central_widget)
|
||||
main_layout = QtWidgets.QVBoxLayout(central_widget)
|
||||
|
||||
# Title label
|
||||
title_label = QtWidgets.QLabel("Library Dashboard", self)
|
||||
title_label.setAlignment(QtCore.Qt.AlignCenter)
|
||||
title_label.setStyleSheet("font-size: 20px; font-weight: bold; color: #0078D4;")
|
||||
main_layout.addWidget(title_label)
|
||||
|
||||
# Available books list
|
||||
available_label = QtWidgets.QLabel("Available Books")
|
||||
available_label.setStyleSheet("font-size: 16px;")
|
||||
main_layout.addWidget(available_label)
|
||||
|
||||
self.available_books_list = QtWidgets.QListWidget()
|
||||
self.available_books_list.addItems(["Book One", "Book Two", "Book Three", "Book Four","Book One", "Book Two", "Book Three", "Book Four","Book One", "Book Two", "Book Three", "Book Four"])
|
||||
self.available_books_list.itemClicked.connect(self.edit_book)
|
||||
main_layout.addWidget(self.available_books_list)
|
||||
|
||||
# Borrowed books list
|
||||
borrowed_label = QtWidgets.QLabel("Currently Borrowed Books")
|
||||
borrowed_label.setStyleSheet("font-size: 16px;")
|
||||
main_layout.addWidget(borrowed_label)
|
||||
|
||||
self.borrowed_books_list = QtWidgets.QListWidget()
|
||||
self.borrowed_books_list.addItems(["Book Two", "Book Four"])
|
||||
self.borrowed_books_list.itemClicked.connect(self.return_book)
|
||||
main_layout.addWidget(self.borrowed_books_list)
|
||||
|
||||
# Buttons for actions
|
||||
button_layout = QtWidgets.QHBoxLayout()
|
||||
register_member_button = QtWidgets.QPushButton("Add New Member")
|
||||
register_member_button.clicked.connect(self.register_member)
|
||||
button_layout.addWidget(register_member_button)
|
||||
|
||||
add_borrow_record_button = QtWidgets.QPushButton("Add Borrow Record")
|
||||
add_borrow_record_button.clicked.connect(self.add_borrow_record)
|
||||
button_layout.addWidget(add_borrow_record_button)
|
||||
|
||||
main_layout.addLayout(button_layout)
|
||||
|
||||
# Slots for button actions
|
||||
def edit_book(self, item):
|
||||
QtWidgets.QMessageBox.information(self, "Edit Book", f"Edit details for '{item.text()}'.")
|
||||
|
||||
def return_book(self, item):
|
||||
QtWidgets.QMessageBox.information(self, "Return Book", f"Mark '{item.text()}' as returned.")
|
||||
|
||||
def register_member(self):
|
||||
QtWidgets.QMessageBox.information(self, "Add Member", "Open dialog to register a new member.")
|
||||
|
||||
def add_borrow_record(self):
|
||||
QtWidgets.QMessageBox.information(self, "Add Borrow Record", "Open dialog to add a borrow record.")
|
||||
|
||||
def create_menu_bar(self):
|
||||
# Create the menu bar
|
||||
menu_bar = self.menuBar()
|
||||
|
||||
# File menu
|
||||
file_menu = menu_bar.addMenu("File")
|
||||
|
||||
import_action = QAction("Import", self)
|
||||
import_action.triggered.connect(self.import_data)
|
||||
file_menu.addAction(import_action)
|
||||
|
||||
|
||||
export_action = QAction("Export", self)
|
||||
export_action.triggered.connect(self.export_data)
|
||||
file_menu.addAction(export_action)
|
||||
|
||||
file_menu.addSeparator()
|
||||
|
||||
exit_action = QAction("Exit", self)
|
||||
exit_action.triggered.connect(self.close)
|
||||
file_menu.addAction(exit_action)
|
||||
|
||||
# Edit menu
|
||||
edit_menu = menu_bar.addMenu("Edit")
|
||||
|
||||
preferences = QAction("Preferences", self)
|
||||
preferences.triggered.connect(self.edit_preferences)
|
||||
edit_menu.addAction(preferences)
|
||||
|
||||
# Help menu
|
||||
help_menu = menu_bar.addMenu("Help")
|
||||
about_action = QAction("About", self)
|
||||
about_action.triggered.connect(self.about)
|
||||
help_menu.addAction(about_action)
|
||||
|
||||
# Menu action slots
|
||||
def edit_preferences(self):
|
||||
dialog = SettingsDialog(parent=self)
|
||||
if dialog.exec() == QtWidgets.QDialog.Accepted:
|
||||
print("Settings were saved.")
|
||||
|
||||
def open_file(self):
|
||||
QtWidgets.QMessageBox.information(self, "Open File", "Open an existing file.")
|
||||
|
||||
def import_data(self):
|
||||
pass
|
||||
|
||||
def export_data(self):
|
||||
pass
|
||||
|
||||
def about(self):
|
||||
QtWidgets.QMessageBox.information(self, "About", "Library Dashboard v1.0\nDeveloped by You.")
|
2
src/ui/qmldir
Normal file
2
src/ui/qmldir
Normal file
@ -0,0 +1,2 @@
|
||||
module Example
|
||||
Main 254.0 Main.qml
|
@ -1,71 +0,0 @@
|
||||
import sys
|
||||
|
||||
from PySide6 import QtWidgets
|
||||
|
||||
class SettingsDialog(QtWidgets.QDialog):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
|
||||
self.setWindowTitle("Settings")
|
||||
self.setMinimumSize(400, 300)
|
||||
|
||||
# Position the dialog relative to the parent (main window)
|
||||
if parent:
|
||||
x = parent.geometry().x() + parent.geometry().width() // 2 - self.width() // 2
|
||||
y = parent.geometry().y() + parent.geometry().height() // 2 - self.height() // 2
|
||||
self.move(x, y)
|
||||
|
||||
# Create layout
|
||||
layout = QtWidgets.QVBoxLayout(self)
|
||||
|
||||
# Checkbox: Enable Notifications
|
||||
self.notifications_checkbox = QtWidgets.QCheckBox("Enable Notifications")
|
||||
layout.addWidget(self.notifications_checkbox)
|
||||
|
||||
# Checkbox: Dark Mode
|
||||
self.dark_mode_checkbox = QtWidgets.QCheckBox("Enable Dark Mode")
|
||||
layout.addWidget(self.dark_mode_checkbox)
|
||||
|
||||
# Dropdown: Language Selection
|
||||
self.language_label = QtWidgets.QLabel("Language:")
|
||||
layout.addWidget(self.language_label)
|
||||
|
||||
self.language_dropdown = QtWidgets.QComboBox()
|
||||
self.language_dropdown.addItems(["English", "Spanish", "French", "German"])
|
||||
layout.addWidget(self.language_dropdown)
|
||||
|
||||
# Dropdown: Theme Selection
|
||||
self.theme_label = QtWidgets.QLabel("Theme:")
|
||||
layout.addWidget(self.theme_label)
|
||||
|
||||
self.theme_dropdown = QtWidgets.QComboBox()
|
||||
self.theme_dropdown.addItems(["Light", "Dark", "System Default"])
|
||||
layout.addWidget(self.theme_dropdown)
|
||||
|
||||
# Buttons
|
||||
button_layout = QtWidgets.QHBoxLayout()
|
||||
|
||||
self.save_button = QtWidgets.QPushButton("Save")
|
||||
self.save_button.clicked.connect(self.save_settings)
|
||||
button_layout.addWidget(self.save_button)
|
||||
|
||||
self.cancel_button = QtWidgets.QPushButton("Cancel")
|
||||
self.cancel_button.clicked.connect(self.reject)
|
||||
button_layout.addWidget(self.cancel_button)
|
||||
|
||||
layout.addLayout(button_layout)
|
||||
|
||||
def save_settings(self):
|
||||
# Example of how to fetch settings
|
||||
notifications = self.notifications_checkbox.isChecked()
|
||||
dark_mode = self.dark_mode_checkbox.isChecked()
|
||||
language = self.language_dropdown.currentText()
|
||||
theme = self.theme_dropdown.currentText()
|
||||
|
||||
print("Settings Saved:")
|
||||
print(f"Notifications: {notifications}")
|
||||
print(f"Dark Mode: {dark_mode}")
|
||||
print(f"Language: {language}")
|
||||
print(f"Theme: {theme}")
|
||||
|
||||
self.accept()
|
Loading…
x
Reference in New Issue
Block a user