|
|
@@ -1,33 +1,28 @@
|
|
|
import os
|
|
|
import sys
|
|
|
-
|
|
|
from PyQt5.QtCore import QSize, Qt
|
|
|
-from PyQt5.QtSql import QSqlDatabase, QSqlTableModel
|
|
|
-from PyQt5.QtWidgets import (
|
|
|
- QApplication,
|
|
|
- QComboBox,
|
|
|
- QDataWidgetMapper,
|
|
|
- QDoubleSpinBox,
|
|
|
- QFormLayout,
|
|
|
- QLabel,
|
|
|
- QLineEdit,
|
|
|
- QMainWindow,
|
|
|
- QSpinBox,
|
|
|
- QWidget,
|
|
|
- QVBoxLayout,
|
|
|
- QHBoxLayout,
|
|
|
- QPushButton,
|
|
|
-)
|
|
|
+from PyQt5.QtSql import QSqlDatabase, QSqlTableModel, QSqlQuery
|
|
|
+from PyQt5.QtWidgets import (QApplication, QComboBox, QDataWidgetMapper,
|
|
|
+ QDoubleSpinBox, QFormLayout, QLabel, QLineEdit,
|
|
|
+ QMainWindow, QSpinBox, QWidget, QVBoxLayout,
|
|
|
+ QHBoxLayout, QPushButton, QMessageBox, QStatusBar)
|
|
|
|
|
|
basedir = os.path.dirname(__file__)
|
|
|
|
|
|
|
|
|
class MainWindow(QMainWindow):
|
|
|
+
|
|
|
def __init__(self):
|
|
|
super().__init__()
|
|
|
+ self.setWindowTitle("Track Editor")
|
|
|
+
|
|
|
+ # Initialize database first
|
|
|
+ if not self.initialize_database():
|
|
|
+ return
|
|
|
|
|
|
form = QFormLayout()
|
|
|
|
|
|
+ # Create widgets
|
|
|
self.track_id = QSpinBox()
|
|
|
self.track_id.setRange(0, 2147483647)
|
|
|
self.track_id.setDisabled(True)
|
|
|
@@ -49,60 +44,124 @@ class MainWindow(QMainWindow):
|
|
|
self.unit_price.setSingleStep(0.01)
|
|
|
self.unit_price.setPrefix("$")
|
|
|
|
|
|
+ # Populate ComboBoxes
|
|
|
+ self.populate_combo_boxes()
|
|
|
+
|
|
|
+ # Add form fields
|
|
|
form.addRow(QLabel("Track ID"), self.track_id)
|
|
|
form.addRow(QLabel("Track name"), self.name)
|
|
|
+ form.addRow(QLabel("Album"), self.album)
|
|
|
+ form.addRow(QLabel("Genre"), self.genre)
|
|
|
form.addRow(QLabel("Composer"), self.composer)
|
|
|
form.addRow(QLabel("Milliseconds"), self.milliseconds)
|
|
|
form.addRow(QLabel("Bytes"), self.bytes)
|
|
|
form.addRow(QLabel("Unit price"), self.unit_price)
|
|
|
|
|
|
- print("driver list: ", ' '.join(QSqlDatabase.drivers()))
|
|
|
- self.db = QSqlDatabase("QSQLITE")
|
|
|
- self.db.setDatabaseName(os.path.join(basedir, "Chinook_Sqlite.sqlite"))
|
|
|
- self.db.open()
|
|
|
-
|
|
|
+ # Setup model and mapper
|
|
|
self.model = QSqlTableModel(db=self.db)
|
|
|
+ self.model.setTable("Track")
|
|
|
+ self.model.select()
|
|
|
|
|
|
self.mapper = QDataWidgetMapper()
|
|
|
self.mapper.setModel(self.model)
|
|
|
-
|
|
|
self.mapper.addMapping(self.track_id, 0)
|
|
|
self.mapper.addMapping(self.name, 1)
|
|
|
+ self.mapper.addMapping(self.album, 2)
|
|
|
+ self.mapper.addMapping(self.genre, 3)
|
|
|
self.mapper.addMapping(self.composer, 5)
|
|
|
self.mapper.addMapping(self.milliseconds, 6)
|
|
|
self.mapper.addMapping(self.bytes, 7)
|
|
|
self.mapper.addMapping(self.unit_price, 8)
|
|
|
|
|
|
- self.model.setTable("Track")
|
|
|
- self.model.select()
|
|
|
-
|
|
|
self.mapper.toFirst()
|
|
|
|
|
|
- self.setMinimumSize(QSize(400, 400))
|
|
|
-
|
|
|
+ # Create controls
|
|
|
controls = QHBoxLayout()
|
|
|
prev_rec = QPushButton("Previous")
|
|
|
- prev_rec.clicked.connect(self.mapper.toPrevious)
|
|
|
+ prev_rec.clicked.connect(self.previous_record)
|
|
|
next_rec = QPushButton("Next")
|
|
|
- next_rec.clicked.connect(self.mapper.toNext)
|
|
|
+ next_rec.clicked.connect(self.next_record)
|
|
|
save_rec = QPushButton("Save Changes")
|
|
|
- save_rec.clicked.connect(self.mapper.submit)
|
|
|
+ save_rec.clicked.connect(self.save_changes)
|
|
|
|
|
|
controls.addWidget(prev_rec)
|
|
|
controls.addWidget(next_rec)
|
|
|
controls.addWidget(save_rec)
|
|
|
|
|
|
- form.setLabelAlignment(
|
|
|
- Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTrailing | Qt.AlignmentFlag.AlignVCenter)
|
|
|
-
|
|
|
+ # Setup layouts
|
|
|
+ form.setLabelAlignment(Qt.AlignmentFlag.AlignLeft
|
|
|
+ | Qt.AlignmentFlag.AlignTrailing
|
|
|
+ | Qt.AlignmentFlag.AlignVCenter)
|
|
|
layout = QVBoxLayout()
|
|
|
layout.addLayout(form)
|
|
|
layout.addLayout(controls)
|
|
|
|
|
|
widget = QWidget()
|
|
|
widget.setLayout(layout)
|
|
|
-
|
|
|
self.setCentralWidget(widget)
|
|
|
+ self.setMinimumSize(QSize(400, 400))
|
|
|
+
|
|
|
+ # Add status bar
|
|
|
+ self.status_bar = QStatusBar()
|
|
|
+ self.setStatusBar(self.status_bar)
|
|
|
+ self.update_status()
|
|
|
+
|
|
|
+ def initialize_database(self):
|
|
|
+ """Initialize database connection with error handling"""
|
|
|
+ self.db = QSqlDatabase.addDatabase("QSQLITE")
|
|
|
+ self.db.setDatabaseName(os.path.join(basedir, "Chinook_Sqlite.sqlite"))
|
|
|
+
|
|
|
+ if not self.db.open():
|
|
|
+ QMessageBox.critical(
|
|
|
+ self, "Database Error",
|
|
|
+ "Could not open database file: " + self.db.lastError().text())
|
|
|
+ return False
|
|
|
+ return True
|
|
|
+
|
|
|
+ def populate_combo_boxes(self):
|
|
|
+ """Populate album and genre combo boxes from database"""
|
|
|
+ # Populate Albums
|
|
|
+ album_query = QSqlQuery("SELECT Title FROM Album ORDER BY Title")
|
|
|
+ while album_query.next():
|
|
|
+ self.album.addItem(album_query.value(0))
|
|
|
+
|
|
|
+ # Populate Genres
|
|
|
+ genre_query = QSqlQuery("SELECT Name FROM Genre ORDER BY Name")
|
|
|
+ while genre_query.next():
|
|
|
+ self.genre.addItem(genre_query.value(0))
|
|
|
+
|
|
|
+ def update_status(self):
|
|
|
+ """Update status bar with current record position"""
|
|
|
+ current = self.mapper.currentIndex() + 1
|
|
|
+ total = self.model.rowCount()
|
|
|
+ self.status_bar.showMessage(f"Record {current} of {total}")
|
|
|
+
|
|
|
+ def previous_record(self):
|
|
|
+ """Navigate to previous record and update status"""
|
|
|
+ self.mapper.toPrevious()
|
|
|
+ self.update_status()
|
|
|
+
|
|
|
+ def next_record(self):
|
|
|
+ """Navigate to next record and update status"""
|
|
|
+ self.mapper.toNext()
|
|
|
+ self.update_status()
|
|
|
+
|
|
|
+ def save_changes(self):
|
|
|
+ """Save changes with validation"""
|
|
|
+ if not self.name.text():
|
|
|
+ QMessageBox.warning(self, "Validation Error",
|
|
|
+ "Track name cannot be empty")
|
|
|
+ return
|
|
|
+
|
|
|
+ if self.mapper.submit():
|
|
|
+ self.status_bar.showMessage("Changes saved successfully", 3000)
|
|
|
+ else:
|
|
|
+ QMessageBox.warning(self, "Save Error", "Failed to save changes")
|
|
|
+
|
|
|
+ def closeEvent(self, event):
|
|
|
+ """Clean up database connection when window closes"""
|
|
|
+ self.db.close()
|
|
|
+ super().closeEvent(event)
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|