diff --git a/components/MusicTable.py b/components/MusicTable.py index 4379328..2ca97c2 100644 --- a/components/MusicTable.py +++ b/components/MusicTable.py @@ -1,24 +1,97 @@ import DBA from PyQt5.QtGui import QStandardItem, QStandardItemModel -from PyQt5.QtWidgets import QTableView +from PyQt5.QtWidgets import QTableView, QApplication +from PyQt5.QtCore import QTimer +from tinytag import TinyTag class MusicTable(QTableView): - def __init__(self): - super().__init__() + def __init__(self, parent=None, qapp=None): + QTableView.__init__(self, parent) + self.headers = ['title', 'artist', 'album', 'genre', 'codec', 'year', 'path'] + self.model = QStandardItemModel() + self.model.setHorizontalHeaderLabels(self.headers) + self.songChanged = None + self.selected_song_filepath = None + self.current_song_filepath = None + self.qapp = None + # self.tableView.resizeColumnsToContents() + self.clicked.connect(self.set_selected_song_filepath) # These are faster than the click/double determination + self.doubleClicked.connect(self.set_current_song_filepath) # These are faster than the click/double determination + self.fetch_library() + + def mousePressEvent(self, event): + self.last = "Click" + QTableView.mousePressEvent(self, event) # Keep original functionality + + def mouseReleaseEvent(self, event): + if self.last == "Click": + QTimer.singleShot(self.qapp.instance().doubleClickInterval(), + self.performSingleClickAction) + else: + # Perform double click action. + self.set_current_song_filepath + self.message = "Double Click" + self.update() + QTableView.mouseReleaseEvent(self, event) # Keep original functionality + + def mouseDoubleClickEvent(self, event): + self.last = "Double Click" + self.doubleClicked.emit(self.selectionModel().currentIndex()) + QTableView.mouseDoubleClickEvent(self, event) # Keep original functionality + + def performSingleClickAction(self): + if self.last == "Click": + self.message = "Click" + self.update() + + def get_selected_rows(self): + """Returns a list of indexes for every selected row""" + rows = [] + for idx in self.selectionModel().siblingAtColumn(): + rows.append(idx.row()) + return rows + + def set_selected_song_filepath(self): + """Sets the filepath of the currently selected song""" + self.selected_song_filepath = self.currentIndex().siblingAtColumn(self.headers.index('path')).data() + print(f'Selected song: {self.selected_song_filepath}') + + def set_current_song_filepath(self): + """Sets the filepath of the currently playing/chosen song""" + self.current_song_filepath = self.currentIndex().siblingAtColumn(self.headers.index('path')).data() + print(f'Current song: {self.current_song_filepath}') + + def get_selected_song_filepath(self): + """Returns the selected song filepath""" + return self.selected_song_filepath + + def get_selected_song_metadata(self): + """Returns the current/chosen song's ID3 tags""" + return TinyTag.get(self.selected_song_filepath) + + def get_current_song_filepath(self): + """Returns the current/chosen song filepath""" + return self.current_song_filepath + + def get_current_song_metadata(self): + """Returns the current/chosen song's ID3 tags""" + return TinyTag.get(self.current_song_filepath) + + + def fetch_library(self): # Fetch library data - with DBA.DBAccess() as db: # returns a tuple, 1 row just for metadata purposes + with DBA.DBAccess() as db: data = db.query('SELECT title, artist, album, genre, codec, album_date, filepath FROM library;', ()) - headers = ['title', 'artist', 'album', 'genre', 'codec', 'year', 'path'] - # Create a model - model = QStandardItemModel() - model.setHorizontalHeaderLabels(headers) # Populate the model for row_data in data: items = [QStandardItem(str(item)) for item in row_data] - model.appendRow(items) - # Set the model to the tableView - self.tableView.setModel(model) - # self.tableView.resizeColumnsToContents() - self.music_table.clicked.connect(self.set_clicked_cell_filepath) + self.model.appendRow(items) + # Set the model to the tableView (we are the tableview) + self.setModel(self.model) + + def load_qapp(self, qapp): + self.qapp = qapp + + \ No newline at end of file diff --git a/main.py b/main.py index ca36107..5d29ef6 100644 --- a/main.py +++ b/main.py @@ -9,24 +9,29 @@ from utils import scan_for_music from utils import initialize_library_database from utils import AudioVisualizer from pyqtgraph import mkBrush +from components import MusicTable class ApplicationWindow(QMainWindow, Ui_MainWindow): - def __init__(self): + def __init__(self, qapp): super(ApplicationWindow, self).__init__() self.setupUi(self) self.setWindowTitle('MusicPom') self.selected_song_filepath = None self.current_song_filepath = None + self.current_song_metadata = None + self.qapp = qapp + print(f'ApplicationWindow self.qapp: {self.qapp}') + self.tableView.load_qapp(self.qapp) global stopped stopped = False # Initialization - self.player = QMediaPlayer(None, QMediaPlayer.VideoSurface) # Audio player + self.player = QMediaPlayer() # Audio player self.probe = QAudioProbe() # Get audio data self.timer = QTimer(self) # Audio timing things - self.model = QStandardItemModel() # Table library listing + # self.model = QStandardItemModel() # Table library listing self.audio_visualizer = AudioVisualizer(self.player) self.current_volume = 50 self.player.setVolume(self.current_volume) @@ -36,33 +41,6 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow): self.probe.setSource(self.player) self.probe.audioBufferProbed.connect(self.process_probe) - # Probably move all the table logic to its own class - # Promote the wigdet to my custom class from utils - # inherit from QTableView - # then i can handle click events for editing metadata in a class - - # current song should also be its own class - # selected song should be its own class too... - - # ______________ - # | | - # | Table making | - # | | - # |______________| - - # Fetch library data - with DBA.DBAccess() as db: - data = db.query('SELECT title, artist, album, genre, codec, album_date, filepath FROM library;', ()) - headers = ['title', 'artist', 'album', 'genre', 'codec', 'year', 'path'] - self.model.setHorizontalHeaderLabels(headers) - for row_data in data: # Populate the model - items = [QStandardItem(str(item)) for item in row_data] - self.model.appendRow(items) - # Set the model to the tableView - self.tableView.setModel(self.model) - # self.tableView.resizeColumnsToContents() - - # Slider Timer (realtime playback feedback horizontal bar) self.timer.start(150) # 150ms update interval solved problem with drag seeking halting playback self.timer.timeout.connect(self.move_slider) @@ -79,27 +57,39 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow): # Connections - self.playbackSlider.sliderMoved[int].connect(lambda: self.player.setPosition(self.playbackSlider.value())) - self.volumeSlider.sliderMoved[int].connect(lambda: self.volume_changed()) - self.playButton.clicked.connect(self.on_play_clicked) + # ! FIXME moving the slider while playing is happening frequently causes playback to halt - the pyqtgraph is also affected by this + self.playbackSlider.sliderMoved[int].connect(lambda: self.player.setPosition(self.playbackSlider.value())) # Move slidet to adjust playback time + self.volumeSlider.sliderMoved[int].connect(lambda: self.volume_changed()) # Move slider to adjust volume + self.playButton.clicked.connect(self.on_play_clicked) # Click to play/pause # self.pauseButton.clicked.connect(self.on_pause_clicked) - self.previousButton.clicked.connect(self.on_previous_clicked) - self.nextButton.clicked.connect(self.on_next_clicked) - self.tableView.clicked.connect(self.set_clicked_cell_filepath) - self.actionPreferences.triggered.connect(self.actionPreferencesClicked) - self.actionScanLibraries.triggered.connect(self.scan_libraries) - self.actionClearDatabase.triggered.connect(initialize_library_database) + self.previousButton.clicked.connect(self.on_previous_clicked) # Click to previous song + self.nextButton.clicked.connect(self.on_next_clicked) # Click to next song + # self.tableView.clicked.connect(self.set_clicked_cell_filepath) + self.actionPreferences.triggered.connect(self.actionPreferencesClicked) # Open preferences menu + self.actionScanLibraries.triggered.connect(self.scan_libraries) # Scan library + self.actionClearDatabase.triggered.connect(initialize_library_database) # Clear database + self.tableView.doubleClicked.connect(self.play_audio_file) # Double click to play song + def play_audio_file(self): - """Start playback of selected track & move playback slider""" - self.current_song_filepath = self.selected_song_filepath - url = QUrl.fromLocalFile(self.current_song_filepath) # read the file + """Start playback of selected track & moves playback slider""" + self.current_song_metadata = self.tableView.get_current_song_metadata() # get metadata + url = QUrl.fromLocalFile(self.tableView.get_current_song_filepath()) # read the file content = QMediaContent(url) # load the audio content - self.player.setMedia(content) # what content to play self.player.play() # play self.move_slider() # mover + # assign metadata + # FIXME when i change tinytag to something else + artist = self.current_song_metadata.artist + album = self.current_song_metadata.album + title = self.current_song_metadata.title + # edit labels + self.artistLabel.setText(artist) + self.albumLabel.setText(album) + self.titleLabel.setText(title) + def update_audio_visualization(self): """Handles upading points on the pyqtgraph visual""" self.clear_audio_visualization() @@ -149,10 +139,6 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow): else: self.play_audio_file() self.playButton.setText("⏸️") - - def set_clicked_cell_filepath(self): - """Sets the filepath of the currently selected song""" - self.selected_song_filepath = self.tableView.currentIndex().siblingAtColumn(6).data() def on_previous_clicked(self): """""" @@ -178,7 +164,8 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow): if __name__ == "__main__": import sys app = QApplication(sys.argv) + print(f'main.py app: {app}') qdarktheme.setup_theme() - ui = ApplicationWindow() + ui = ApplicationWindow(app) ui.show() sys.exit(app.exec_()) \ No newline at end of file diff --git a/ui.py b/ui.py index 84c9289..cbcd2d4 100644 --- a/ui.py +++ b/ui.py @@ -14,12 +14,12 @@ from PyQt5 import QtCore, QtGui, QtWidgets class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") - MainWindow.resize(1061, 795) + MainWindow.resize(1062, 795) MainWindow.setStatusTip("") self.centralwidget = QtWidgets.QWidget(MainWindow) self.centralwidget.setObjectName("centralwidget") self.frame = QtWidgets.QFrame(self.centralwidget) - self.frame.setGeometry(QtCore.QRect(0, 160, 1061, 501)) + self.frame.setGeometry(QtCore.QRect(0, 160, 1061, 461)) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Ignored, QtWidgets.QSizePolicy.Ignored) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) @@ -28,8 +28,8 @@ class Ui_MainWindow(object): self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel) self.frame.setFrameShadow(QtWidgets.QFrame.Raised) self.frame.setObjectName("frame") - self.tableView = QtWidgets.QTableView(self.frame) - self.tableView.setGeometry(QtCore.QRect(0, 0, 1061, 501)) + self.tableView = MusicTable(self.frame) + self.tableView.setGeometry(QtCore.QRect(0, 0, 1061, 461)) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) @@ -37,6 +37,7 @@ class Ui_MainWindow(object): self.tableView.setSizePolicy(sizePolicy) self.tableView.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) self.tableView.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents) + self.tableView.setEditTriggers(QtWidgets.QAbstractItemView.AnyKeyPressed|QtWidgets.QAbstractItemView.EditKeyPressed) self.tableView.setAlternatingRowColors(True) self.tableView.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) self.tableView.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) @@ -77,37 +78,15 @@ class Ui_MainWindow(object): self.PlotWidget = PlotWidget(self.frame_4) self.PlotWidget.setGeometry(QtCore.QRect(0, 0, 521, 121)) self.PlotWidget.setObjectName("PlotWidget") - self.horizontalLayoutWidget_2 = QtWidgets.QWidget(self.centralwidget) - self.horizontalLayoutWidget_2.setGeometry(QtCore.QRect(0, 660, 1061, 60)) - self.horizontalLayoutWidget_2.setObjectName("horizontalLayoutWidget_2") - self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget_2) - self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0) - self.horizontalLayout_2.setObjectName("horizontalLayout_2") - self.previousButton = QtWidgets.QPushButton(self.horizontalLayoutWidget_2) - font = QtGui.QFont() - font.setPointSize(28) - self.previousButton.setFont(font) - self.previousButton.setObjectName("previousButton") - self.horizontalLayout_2.addWidget(self.previousButton) - self.playButton = QtWidgets.QPushButton(self.horizontalLayoutWidget_2) - font = QtGui.QFont() - font.setPointSize(28) - self.playButton.setFont(font) - self.playButton.setObjectName("playButton") - self.horizontalLayout_2.addWidget(self.playButton) - self.nextButton = QtWidgets.QPushButton(self.horizontalLayoutWidget_2) - font = QtGui.QFont() - font.setPointSize(28) - self.nextButton.setFont(font) - self.nextButton.setObjectName("nextButton") - self.horizontalLayout_2.addWidget(self.nextButton) self.frame_3 = QtWidgets.QFrame(self.centralwidget) - self.frame_3.setGeometry(QtCore.QRect(0, 720, 1061, 31)) + self.frame_3.setGeometry(QtCore.QRect(0, 680, 1061, 61)) self.frame_3.setFrameShape(QtWidgets.QFrame.StyledPanel) self.frame_3.setFrameShadow(QtWidgets.QFrame.Raised) self.frame_3.setObjectName("frame_3") self.volumeSlider = QtWidgets.QSlider(self.frame_3) - self.volumeSlider.setGeometry(QtCore.QRect(10, 0, 181, 31)) + self.volumeSlider.setGeometry(QtCore.QRect(10, 10, 181, 31)) + self.volumeSlider.setMaximum(100) + self.volumeSlider.setProperty("value", 50) self.volumeSlider.setOrientation(QtCore.Qt.Horizontal) self.volumeSlider.setObjectName("volumeSlider") self.frame_5 = QtWidgets.QFrame(self.centralwidget) @@ -144,9 +123,33 @@ class Ui_MainWindow(object): self.albumLabel.setFont(font) self.albumLabel.setObjectName("albumLabel") self.verticalLayout.addWidget(self.albumLabel) + self.horizontalLayoutWidget_2 = QtWidgets.QWidget(self.centralwidget) + self.horizontalLayoutWidget_2.setGeometry(QtCore.QRect(0, 620, 1061, 60)) + self.horizontalLayoutWidget_2.setObjectName("horizontalLayoutWidget_2") + self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget_2) + self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0) + self.horizontalLayout_2.setObjectName("horizontalLayout_2") + self.previousButton = QtWidgets.QPushButton(self.horizontalLayoutWidget_2) + font = QtGui.QFont() + font.setPointSize(28) + self.previousButton.setFont(font) + self.previousButton.setObjectName("previousButton") + self.horizontalLayout_2.addWidget(self.previousButton) + self.playButton = QtWidgets.QPushButton(self.horizontalLayoutWidget_2) + font = QtGui.QFont() + font.setPointSize(28) + self.playButton.setFont(font) + self.playButton.setObjectName("playButton") + self.horizontalLayout_2.addWidget(self.playButton) + self.nextButton = QtWidgets.QPushButton(self.horizontalLayoutWidget_2) + font = QtGui.QFont() + font.setPointSize(28) + self.nextButton.setFont(font) + self.nextButton.setObjectName("nextButton") + self.horizontalLayout_2.addWidget(self.nextButton) MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtWidgets.QMenuBar(MainWindow) - self.menubar.setGeometry(QtCore.QRect(0, 0, 1061, 24)) + self.menubar.setGeometry(QtCore.QRect(0, 0, 1062, 24)) self.menubar.setObjectName("menubar") self.menuFile = QtWidgets.QMenu(self.menubar) self.menuFile.setObjectName("menuFile") @@ -183,12 +186,12 @@ class Ui_MainWindow(object): self.startTimeLabel.setText(_translate("MainWindow", "00:00")) self.slashLabel.setText(_translate("MainWindow", "/")) self.endTimeLabel.setText(_translate("MainWindow", "00:00")) - self.previousButton.setText(_translate("MainWindow", "⏮️")) - self.playButton.setText(_translate("MainWindow", "▶️")) - self.nextButton.setText(_translate("MainWindow", "⏭️")) self.artistLabel.setText(_translate("MainWindow", "artist")) self.titleLabel.setText(_translate("MainWindow", "song title")) self.albumLabel.setText(_translate("MainWindow", "album")) + self.previousButton.setText(_translate("MainWindow", "⏮️")) + self.playButton.setText(_translate("MainWindow", "▶️")) + self.nextButton.setText(_translate("MainWindow", "⏭️")) self.menuFile.setTitle(_translate("MainWindow", "File")) self.menuEdit.setTitle(_translate("MainWindow", "Edit")) self.menuView.setTitle(_translate("MainWindow", "View")) @@ -197,4 +200,5 @@ class Ui_MainWindow(object): self.actionPreferences.setStatusTip(_translate("MainWindow", "Open preferences")) self.actionScanLibraries.setText(_translate("MainWindow", "Scan libraries")) self.actionClearDatabase.setText(_translate("MainWindow", "Clear Database")) +from components import MusicTable from pyqtgraph import PlotWidget diff --git a/ui.ui b/ui.ui index 1b1a938..0e7da19 100644 --- a/ui.ui +++ b/ui.ui @@ -6,7 +6,7 @@ 0 0 - 1061 + 1062 795 @@ -23,7 +23,7 @@ 0 160 1061 - 501 + 461 @@ -38,13 +38,13 @@ QFrame::Raised - + 0 0 1061 - 501 + 461 @@ -59,6 +59,9 @@ QAbstractScrollArea::AdjustToContents + + QAbstractItemView::AnyKeyPressed|QAbstractItemView::EditKeyPressed + true @@ -165,61 +168,13 @@ - - - - 0 - 660 - 1061 - 60 - - - - - - - - 28 - - - - ⏮️ - - - - - - - - 28 - - - - ▶️ - - - - - - - - 28 - - - - ⏭️ - - - - - 0 - 720 + 680 1061 - 31 + 61 @@ -232,11 +187,17 @@ 10 - 0 + 10 181 31 + + 100 + + + 50 + Qt::Horizontal @@ -311,13 +272,61 @@ + + + + 0 + 620 + 1061 + 60 + + + + + + + + 28 + + + + ⏮️ + + + + + + + + 28 + + + + ▶️ + + + + + + + + 28 + + + + ⏭️ + + + + + 0 0 - 1061 + 1062 24 @@ -376,6 +385,11 @@
pyqtgraph
1 + + MusicTable + QTableView +
components
+