Compare commits
No commits in common. "12828b268851dd07f324292b756638871a3905dc" and "a2ffd8aa3b8717571f93b501d5e8340f754dc3fe" have entirely different histories.
12828b2688
...
a2ffd8aa3b
@ -1,5 +1,4 @@
|
||||
DATABASE_HOST=
|
||||
DATABASE_PORT=
|
||||
DATABASE_NAME=
|
||||
DATABASE_USER=
|
||||
DATABASE_PASSWORD=
|
||||
DB_USER=
|
||||
DB_NAME=
|
||||
DB_PASSWORD=
|
||||
DB_HOST=
|
@ -1,7 +1,6 @@
|
||||
import sys
|
||||
import os
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
from PySide6.QtWidgets import QMessageBox, QApplication
|
||||
|
||||
@ -14,14 +13,14 @@ from ui.window import LibraryWindow
|
||||
|
||||
|
||||
class LibraryApp():
|
||||
def __init__(self, user_config: Optional[UserConfig] = None):
|
||||
def __init__(self, user_config: UserConfig | None = None):
|
||||
setup_logger()
|
||||
self.logger = logging.getLogger(__name__)
|
||||
self.logger.info("Starting")
|
||||
|
||||
|
||||
self.qt_app = QApplication([])
|
||||
self.user_config = user_config
|
||||
|
||||
|
||||
try:
|
||||
self.database_manger = DatabaseManager()
|
||||
except DatabaseConfigError as e:
|
||||
@ -38,7 +37,7 @@ class LibraryApp():
|
||||
self.window.show()
|
||||
status = self.qt_app.exec()
|
||||
self.cleanup()
|
||||
self.logger.info("Exiting")
|
||||
self.logger.info("Exitting")
|
||||
return status
|
||||
|
||||
def show_error(self, text: str, detail_text: str = ""):
|
||||
|
@ -1,40 +1,19 @@
|
||||
from PySide6.QtGui import QGuiApplication, QAction
|
||||
from PySide6.QtQml import QQmlApplicationEngine
|
||||
from PySide6 import QtWidgets, QtCore
|
||||
from PySide6.QtWidgets import QDialog, QVBoxLayout, QHBoxLayout, QLabel
|
||||
|
||||
from models.book_overview import BooksOverview
|
||||
class BookEditor(QtWidgets.QWidget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
class BookEditor(QDialog):
|
||||
def __init__(self, book_overview: BooksOverview, parent=None):
|
||||
super().__init__(parent)
|
||||
# Central widget and layout
|
||||
main_layout = QtWidgets.QVBoxLayout(self)
|
||||
|
||||
self.setWindowTitle("Edit a book")
|
||||
self.setMinimumSize(400, 300)
|
||||
# Title label
|
||||
title_label = QtWidgets.QLabel("Books", self)
|
||||
title_label.setAlignment(QtCore.Qt.AlignCenter)
|
||||
title_label.setStyleSheet("font-size: 20px; font-weight: bold; color: #0078D4;")
|
||||
main_layout.addWidget(title_label)
|
||||
|
||||
# Create layout
|
||||
layout = QVBoxLayout(self)
|
||||
self.setLayout(main_layout)
|
||||
|
||||
data_mode_layout = QHBoxLayout()
|
||||
|
||||
self.data_mode_label = QLabel("Data mode:")
|
||||
data_mode_layout.addWidget(self.data_mode_label)
|
||||
|
||||
|
||||
|
||||
# Buttons
|
||||
button_layout = QtWidgets.QHBoxLayout()
|
||||
|
||||
self.save_button = QtWidgets.QPushButton("Save")
|
||||
self.save_button.clicked.connect(self.save_book)
|
||||
button_layout.addWidget(self.save_button)
|
||||
|
||||
self.cancel_button = QtWidgets.QPushButton("Discard")
|
||||
self.cancel_button.clicked.connect(self.reject)
|
||||
button_layout.addWidget(self.cancel_button)
|
||||
|
||||
layout.addLayout(button_layout)
|
||||
|
||||
|
||||
def save_book(self):
|
||||
pass
|
@ -1,11 +1,9 @@
|
||||
from PySide6.QtGui import QGuiApplication, QAction, Qt
|
||||
from PySide6.QtGui import QGuiApplication, QAction
|
||||
from PySide6.QtQml import QQmlApplicationEngine
|
||||
from PySide6.QtWidgets import QHBoxLayout, QVBoxLayout, QLabel, QWidget, QMenu, QSizePolicy, QLayout
|
||||
from PySide6.QtCore import qDebug
|
||||
|
||||
from ui.book_editor.book_editor import BookEditor
|
||||
|
||||
from models.book import BookStatusEnum
|
||||
|
||||
from models.book_overview import BooksOverview
|
||||
|
||||
STATUS_TO_COLOR_MAP = {
|
||||
@ -13,36 +11,22 @@ STATUS_TO_COLOR_MAP = {
|
||||
BookStatusEnum.borrowed: "#702525",
|
||||
BookStatusEnum.reserved: "#bc7613"
|
||||
}
|
||||
|
||||
class BookCard(QWidget):
|
||||
def __init__(self, book_overview: BooksOverview):
|
||||
super().__init__()
|
||||
|
||||
self.book_overview = book_overview
|
||||
|
||||
self.setAttribute(Qt.WidgetAttribute.WA_Hover, True) # Enable hover events
|
||||
self.setAttribute(Qt.WidgetAttribute.WA_StyledBackground, True) # Enable styling for background
|
||||
|
||||
# Set initial stylesheet with hover behavior
|
||||
self.setStyleSheet("""
|
||||
BookCard:hover {
|
||||
background-color: palette(highlight);
|
||||
}
|
||||
""")
|
||||
|
||||
# Layout setup
|
||||
# Create the layout for the card
|
||||
layout = QHBoxLayout(self)
|
||||
layout.setSizeConstraint(QLayout.SizeConstraint.SetMinimumSize)
|
||||
self.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed)
|
||||
layout.setSizeConstraint(QLayout.SizeConstraint.SetMinimumSize) # Ensure minimum size is respected
|
||||
self.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed) # Avoid vertical stretching
|
||||
|
||||
layout.setContentsMargins(10, 10, 10, 10)
|
||||
layout.setContentsMargins(10, 10, 10, 10) # Optional: Add margins for spacing
|
||||
layout.setSpacing(10)
|
||||
|
||||
# Left-side content
|
||||
left_side = QVBoxLayout()
|
||||
layout.addLayout(left_side)
|
||||
title_label = QLabel(book_overview.title)
|
||||
title_label.setStyleSheet("font-size: 20px; font-weight: bold;")
|
||||
author_label = QLabel("By: " + book_overview.author_name)
|
||||
isbn_label = QLabel("ISBN: " + (book_overview.isbn or "Not Available"))
|
||||
left_side.addWidget(title_label)
|
||||
@ -52,50 +36,33 @@ class BookCard(QWidget):
|
||||
# Right-side content
|
||||
right_side = QVBoxLayout()
|
||||
layout.addLayout(right_side)
|
||||
|
||||
status_label = QLabel(str(book_overview.status.value.capitalize()))
|
||||
status_label.setStyleSheet(f"color: {STATUS_TO_COLOR_MAP[book_overview.status]}; font-size: 20px; font-weight: bold;")
|
||||
status_label.setAlignment(Qt.AlignmentFlag.AlignRight)
|
||||
|
||||
status_label = QLabel(str(book_overview.status))
|
||||
status_label.setStyleSheet(f"color: {STATUS_TO_COLOR_MAP[book_overview.status]}")
|
||||
right_side.addWidget(status_label)
|
||||
|
||||
if book_overview.librarian_name and book_overview.borrower_name:
|
||||
borrower_label = QLabel("Borrowed: " + book_overview.borrower_name)
|
||||
borrower_label.setAlignment(Qt.AlignmentFlag.AlignRight)
|
||||
|
||||
librarian_label = QLabel("By: " + book_overview.librarian_name)
|
||||
librarian_label.setAlignment(Qt.AlignmentFlag.AlignRight)
|
||||
|
||||
right_side.addWidget(borrower_label)
|
||||
right_side.addWidget(librarian_label)
|
||||
|
||||
self.setLayout(layout)
|
||||
|
||||
def mousePressEvent(self, event):
|
||||
if event.button() == Qt.MouseButton.LeftButton:
|
||||
self.contextMenuEvent(event)
|
||||
else:
|
||||
super().mousePressEvent(event)
|
||||
|
||||
def contextMenuEvent(self, event):
|
||||
"""Override to create a custom right-click menu."""
|
||||
context_menu = QMenu(self)
|
||||
|
||||
# Add actions to the menu
|
||||
action_edit_book = context_menu.addAction("Edit Book")
|
||||
action_edit_author = context_menu.addAction("Edit Author")
|
||||
action_mark_returned = context_menu.addAction("Mark as Returned")
|
||||
|
||||
if self.book_overview.status == BookStatusEnum.borrowed:
|
||||
action_mark_returned = context_menu.addAction("Mark as Returned")
|
||||
|
||||
if self.book_overview.status == BookStatusEnum.reserved:
|
||||
action_remove_reservation = context_menu.addAction("Remove reservation")
|
||||
|
||||
# Execute the menu and get the selected action
|
||||
action = context_menu.exec_(self.mapToGlobal(event.pos()))
|
||||
|
||||
if action == action_edit_book:
|
||||
BookEditor(self.book_overview).exec()
|
||||
self.label.setText("Edit Book selected")
|
||||
elif action == action_edit_author:
|
||||
print("Edit Author selected")
|
||||
self.label.setText("Edit Author selected")
|
||||
elif action == action_mark_returned:
|
||||
print("Mark as Returned selected")
|
||||
elif action == action_remove_reservation:
|
||||
print("Remove reservation selected")
|
||||
self.label.setText("Mark as Returned selected")
|
@ -1,16 +1,15 @@
|
||||
from PySide6.QtGui import QAction
|
||||
from PySide6.QtWidgets import (
|
||||
QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QScrollArea,
|
||||
QFrame, QPushButton, QMessageBox, QVBoxLayout
|
||||
QFrame, QPushButton, QMessageBox, QVBoxLayout, QScrollArea
|
||||
)
|
||||
from PySide6.QtCore import Qt
|
||||
|
||||
from .book_card import BookCard
|
||||
from models.book_overview import BooksOverview
|
||||
|
||||
from utils.database import DatabaseManager
|
||||
|
||||
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
class LibraryDashboard(QWidget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
@ -21,8 +20,7 @@ class LibraryDashboard(QWidget):
|
||||
# Title label
|
||||
title_label = QLabel("Dashboard", self)
|
||||
title_label.setAlignment(Qt.AlignCenter)
|
||||
title_label.setStyleSheet(
|
||||
"font-size: 20px; font-weight: bold; color: #0078D4;")
|
||||
title_label.setStyleSheet("font-size: 20px; font-weight: bold; color: #0078D4;")
|
||||
main_layout.addWidget(title_label)
|
||||
|
||||
# Search bar
|
||||
@ -38,21 +36,17 @@ class LibraryDashboard(QWidget):
|
||||
# Container widget for the scroll area
|
||||
self.scroll_widget = QWidget()
|
||||
self.scroll_layout = QVBoxLayout(self.scroll_widget)
|
||||
self.scroll_layout.setSpacing(5) # Set gap between individual cards
|
||||
self.scroll_layout.setContentsMargins(0, 0, 0, 0) # Remove spacing from all sides which is present by default
|
||||
|
||||
# Align the cards to the top
|
||||
self.scroll_layout.setAlignment(Qt.AlignTop)
|
||||
self.scroll_layout.setSpacing(5) # Optional: Adjust spacing between cards
|
||||
self.scroll_layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
# Example cards
|
||||
self.books = self.fetch_books_from_db()
|
||||
self.book_cards = []
|
||||
# self.books = self.fetch_books_from_db()
|
||||
# self.book_cards = []
|
||||
|
||||
for book in self.books:
|
||||
card = BookCard(book)
|
||||
|
||||
self.scroll_layout.addWidget(card)
|
||||
self.book_cards.append(card)
|
||||
# for book in self.books:
|
||||
# card = BookCard(book)
|
||||
# self.scroll_layout.addWidget(card)
|
||||
# self.book_cards.append(card)
|
||||
|
||||
self.scroll_widget.setLayout(self.scroll_layout)
|
||||
self.scroll_area.setWidget(self.scroll_widget)
|
||||
@ -73,23 +67,21 @@ class LibraryDashboard(QWidget):
|
||||
def filter_books(self, text):
|
||||
"""Filter the cards based on the search input."""
|
||||
for card, book in zip(self.book_cards, self.books):
|
||||
card.setVisible(text.lower() in book.title.lower())
|
||||
card.setVisible(text.lower() in book["title"].lower() or text.lower() in book["description"].lower())
|
||||
|
||||
def register_member(self):
|
||||
QMessageBox.information(
|
||||
self, "Add Member", "Open dialog to register a new member.")
|
||||
QMessageBox.information(self, "Add Member", "Open dialog to register a new member.")
|
||||
|
||||
def add_borrow_record(self):
|
||||
QMessageBox.information(self, "Add Borrow Record",
|
||||
"Open dialog to add a borrow record.")
|
||||
QMessageBox.information(self, "Add Borrow Record", "Open dialog to add a borrow record.")
|
||||
|
||||
def fetch_books_from_db(self):
|
||||
|
||||
"""Fetch all books from the database."""
|
||||
try:
|
||||
with DatabaseManager.get_session() as session:
|
||||
books = session.query(BooksOverview).all()
|
||||
return books
|
||||
session = SessionLocal()
|
||||
books = session.query(BooksOverview).all()
|
||||
return books
|
||||
except Exception as e:
|
||||
QMessageBox.critical(self, "Database Error",
|
||||
f"Failed to fetch books: {e}")
|
||||
return []
|
||||
QMessageBox.critical(self, "Database Error", f"Failed to fetch books: {e}")
|
||||
return []
|
@ -11,6 +11,12 @@ class SettingsDialog(QtWidgets.QDialog):
|
||||
self.setWindowTitle("Settings")
|
||||
self.setMinimumSize(400, 100)
|
||||
|
||||
# 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)
|
||||
|
||||
|
@ -26,7 +26,7 @@ class LibraryWindow(QtWidgets.QMainWindow):
|
||||
self.setCentralWidget(central_widget)
|
||||
|
||||
central_widget.addTab(LibraryDashboard(), "Dashboard")
|
||||
# central_widget.addTab(BookEditor(), "Books")
|
||||
central_widget.addTab(BookEditor(), "Books")
|
||||
central_widget.addTab(MemberEditor(), "Members")
|
||||
|
||||
|
||||
@ -74,7 +74,6 @@ class LibraryWindow(QtWidgets.QMainWindow):
|
||||
# Edit menu
|
||||
edit_menu = menu_bar.addMenu("Edit")
|
||||
|
||||
# Preferences menu
|
||||
preferences_action = QAction("Preferences", self)
|
||||
preferences_action.setShortcut("Ctrl+,")
|
||||
preferences_action.triggered.connect(self.edit_preferences)
|
||||
|
@ -1,33 +1,27 @@
|
||||
import logging
|
||||
|
||||
from sqlalchemy.orm import sessionmaker, Session
|
||||
from sqlalchemy import create_engine
|
||||
from mysql.connector import connect, cursor
|
||||
|
||||
from utils.config import DatabaseConfig
|
||||
from utils.errors.database_config_error import DatabaseConfigError
|
||||
|
||||
|
||||
class DatabaseManager():
|
||||
|
||||
_instance: 'DatabaseManager' = None
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
if cls._instance is None:
|
||||
cls._instance = super().__new__(cls)
|
||||
return cls._instance
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.logger = logging.getLogger(__name__)
|
||||
self.logger.info("Reading database config")
|
||||
self.database_config = DatabaseConfig()
|
||||
self.engine = create_engine(f'mysql+mysqlconnector://{self.database_config.user}:{
|
||||
self.database_config.password}@{self.database_config.host}/{self.database_config.name}')
|
||||
self.session_local = sessionmaker(bind=self.engine)
|
||||
self.connection = connect(
|
||||
host=self.database_config.host,
|
||||
port=self.database_config.port,
|
||||
database=self.database_config.name,
|
||||
user=self.database_config.user,
|
||||
password=self.database_config.password
|
||||
)
|
||||
|
||||
def get_cursor(self) -> cursor.MySQLCursorAbstract:
|
||||
return self.connection.cursor(dictionary=True)
|
||||
|
||||
def cleanup(self) -> None:
|
||||
self.logger.debug("Closing connection")
|
||||
self.engine.dispose()
|
||||
|
||||
@classmethod
|
||||
def get_session(cls) -> Session:
|
||||
return DatabaseManager._instance.session_local()
|
||||
self.connection.close()
|
||||
|
Loading…
x
Reference in New Issue
Block a user