| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172 |
- import os
- import sys
- from PyQt5.QtCore import QSize, Qt
- 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)
- self.name = QLineEdit()
- self.album = QComboBox()
- self.genre = QComboBox()
- self.composer = QLineEdit()
- self.milliseconds = QSpinBox()
- self.milliseconds.setRange(0, 2147483647)
- self.milliseconds.setSingleStep(1)
- self.bytes = QSpinBox()
- self.bytes.setRange(0, 2147483647)
- self.bytes.setSingleStep(1)
- self.unit_price = QDoubleSpinBox()
- self.unit_price.setRange(0, 999)
- 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)
- # 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.mapper.toFirst()
- # Create controls
- controls = QHBoxLayout()
- prev_rec = QPushButton("Previous")
- prev_rec.clicked.connect(self.previous_record)
- next_rec = QPushButton("Next")
- next_rec.clicked.connect(self.next_record)
- save_rec = QPushButton("Save Changes")
- save_rec.clicked.connect(self.save_changes)
- controls.addWidget(prev_rec)
- controls.addWidget(next_rec)
- controls.addWidget(save_rec)
- # 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__":
- app = QApplication(sys.argv)
- window = MainWindow()
- window.show()
- sys.exit(app.exec_())
|