widget_mapper.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. import os
  2. import sys
  3. from PyQt5.QtCore import QSize, Qt
  4. from PyQt5.QtSql import QSqlDatabase, QSqlTableModel, QSqlQuery
  5. from PyQt5.QtWidgets import (QApplication, QComboBox, QDataWidgetMapper,
  6. QDoubleSpinBox, QFormLayout, QLabel, QLineEdit,
  7. QMainWindow, QSpinBox, QWidget, QVBoxLayout,
  8. QHBoxLayout, QPushButton, QMessageBox, QStatusBar)
  9. basedir = os.path.dirname(__file__)
  10. class MainWindow(QMainWindow):
  11. def __init__(self):
  12. super().__init__()
  13. self.setWindowTitle("Track Editor")
  14. # Initialize database first
  15. if not self.initialize_database():
  16. return
  17. form = QFormLayout()
  18. # Create widgets
  19. self.track_id = QSpinBox()
  20. self.track_id.setRange(0, 2147483647)
  21. self.track_id.setDisabled(True)
  22. self.name = QLineEdit()
  23. self.album = QComboBox()
  24. self.genre = QComboBox()
  25. self.composer = QLineEdit()
  26. self.milliseconds = QSpinBox()
  27. self.milliseconds.setRange(0, 2147483647)
  28. self.milliseconds.setSingleStep(1)
  29. self.bytes = QSpinBox()
  30. self.bytes.setRange(0, 2147483647)
  31. self.bytes.setSingleStep(1)
  32. self.unit_price = QDoubleSpinBox()
  33. self.unit_price.setRange(0, 999)
  34. self.unit_price.setSingleStep(0.01)
  35. self.unit_price.setPrefix("$")
  36. # Populate ComboBoxes
  37. self.populate_combo_boxes()
  38. # Add form fields
  39. form.addRow(QLabel("Track ID"), self.track_id)
  40. form.addRow(QLabel("Track name"), self.name)
  41. form.addRow(QLabel("Album"), self.album)
  42. form.addRow(QLabel("Genre"), self.genre)
  43. form.addRow(QLabel("Composer"), self.composer)
  44. form.addRow(QLabel("Milliseconds"), self.milliseconds)
  45. form.addRow(QLabel("Bytes"), self.bytes)
  46. form.addRow(QLabel("Unit price"), self.unit_price)
  47. # Setup model and mapper
  48. self.model = QSqlTableModel(db=self.db)
  49. self.model.setTable("Track")
  50. self.model.select()
  51. self.mapper = QDataWidgetMapper()
  52. self.mapper.setModel(self.model)
  53. self.mapper.addMapping(self.track_id, 0)
  54. self.mapper.addMapping(self.name, 1)
  55. self.mapper.addMapping(self.album, 2)
  56. self.mapper.addMapping(self.genre, 3)
  57. self.mapper.addMapping(self.composer, 5)
  58. self.mapper.addMapping(self.milliseconds, 6)
  59. self.mapper.addMapping(self.bytes, 7)
  60. self.mapper.addMapping(self.unit_price, 8)
  61. self.mapper.toFirst()
  62. # Create controls
  63. controls = QHBoxLayout()
  64. prev_rec = QPushButton("Previous")
  65. prev_rec.clicked.connect(self.previous_record)
  66. next_rec = QPushButton("Next")
  67. next_rec.clicked.connect(self.next_record)
  68. save_rec = QPushButton("Save Changes")
  69. save_rec.clicked.connect(self.save_changes)
  70. controls.addWidget(prev_rec)
  71. controls.addWidget(next_rec)
  72. controls.addWidget(save_rec)
  73. # Setup layouts
  74. form.setLabelAlignment(Qt.AlignmentFlag.AlignLeft
  75. | Qt.AlignmentFlag.AlignTrailing
  76. | Qt.AlignmentFlag.AlignVCenter)
  77. layout = QVBoxLayout()
  78. layout.addLayout(form)
  79. layout.addLayout(controls)
  80. widget = QWidget()
  81. widget.setLayout(layout)
  82. self.setCentralWidget(widget)
  83. self.setMinimumSize(QSize(400, 400))
  84. # Add status bar
  85. self.status_bar = QStatusBar()
  86. self.setStatusBar(self.status_bar)
  87. self.update_status()
  88. def initialize_database(self):
  89. """Initialize database connection with error handling"""
  90. self.db = QSqlDatabase.addDatabase("QSQLITE")
  91. self.db.setDatabaseName(os.path.join(basedir, "Chinook_Sqlite.sqlite"))
  92. if not self.db.open():
  93. QMessageBox.critical(
  94. self, "Database Error",
  95. "Could not open database file: " + self.db.lastError().text())
  96. return False
  97. return True
  98. def populate_combo_boxes(self):
  99. """Populate album and genre combo boxes from database"""
  100. # Populate Albums
  101. album_query = QSqlQuery("SELECT Title FROM Album ORDER BY Title")
  102. while album_query.next():
  103. self.album.addItem(album_query.value(0))
  104. # Populate Genres
  105. genre_query = QSqlQuery("SELECT Name FROM Genre ORDER BY Name")
  106. while genre_query.next():
  107. self.genre.addItem(genre_query.value(0))
  108. def update_status(self):
  109. """Update status bar with current record position"""
  110. current = self.mapper.currentIndex() + 1
  111. total = self.model.rowCount()
  112. self.status_bar.showMessage(f"Record {current} of {total}")
  113. def previous_record(self):
  114. """Navigate to previous record and update status"""
  115. self.mapper.toPrevious()
  116. self.update_status()
  117. def next_record(self):
  118. """Navigate to next record and update status"""
  119. self.mapper.toNext()
  120. self.update_status()
  121. def save_changes(self):
  122. """Save changes with validation"""
  123. if not self.name.text():
  124. QMessageBox.warning(self, "Validation Error",
  125. "Track name cannot be empty")
  126. return
  127. if self.mapper.submit():
  128. self.status_bar.showMessage("Changes saved successfully", 3000)
  129. else:
  130. QMessageBox.warning(self, "Save Error", "Failed to save changes")
  131. def closeEvent(self, event):
  132. """Clean up database connection when window closes"""
  133. self.db.close()
  134. super().closeEvent(event)
  135. if __name__ == "__main__":
  136. app = QApplication(sys.argv)
  137. window = MainWindow()
  138. window.show()
  139. sys.exit(app.exec_())