#!/usr/bin/python3 from PyQt6.QtCore import Qt, pyqtSignal from PyQt6.QtGui import QDropEvent, QIcon from PyQt6.QtWidgets import QTreeWidget, QAbstractItemView from .track import TrackItem class PlaylistView(QTreeWidget): itemDropped = pyqtSignal(QTreeWidget, list) sort_signal = pyqtSignal(int, Qt.SortOrder) playing_mark = QIcon.fromTheme(QIcon.ThemeIcon.MediaPlaybackStart) def __init__(self, playlist, dock, parent=None): super().__init__(parent) self.playlist = playlist self.library_dock = dock self.app = playlist.app self.header = self.header() self.header.setSectionsClickable(True) self.header.setSortIndicatorShown(True) playlist.views[id(dock)] = self self.setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection) self.setColumnCount(4) headers = [ "#", "Title", "Artist", "Album", "# Custom Sorting" ] self.setHeaderLabels(headers) self.itemActivated.connect(self.on_track_activation) self.header.sectionClicked.connect(self.on_header_click) self.sort_signal.connect(self.sortItems) def on_header_click(self, section_index: int): if section_index == 0: # this would just invert the current sorting return sorting = self.playlist.sorting last_sort_section_index, order = sorting[4] if last_sort_section_index == section_index: order = not order # invert order self.playlist.sorting[4] = (section_index, order) # set sorting # convert True/False to Qt.SortOrder.AscendingOrder/Qt.SortOrder.DescendingOrder qorder = Qt.SortOrder.AscendingOrder if order else Qt.SortOrder.DescendingOrder self.header.setSortIndicator(section_index, qorder) else: del sorting[0] # remove first sort sorting.append((section_index, True)) # last sort is this section index, ascending self.header.setSortIndicator(section_index, Qt.SortOrder.AscendingOrder) self.sort() def sort(self): for index, order in self.playlist.sorting: # convert True/False to Qt.SortOrder.AscendingOrder/Qt.SortOrder.DescendingOrder qorder = Qt.SortOrder.AscendingOrder if order else Qt.SortOrder.DescendingOrder # somehow, QTreeWidget.sortItems() cant be called from a thread, so we have to use a signal to execute it # in the main thread self.sort_signal.emit(index, qorder) # self.sortItems(index, qorder) self.on_sort() def on_sort(self, user_sort: bool=False): num_tracks = self.topLevelItemCount() i = 0 while i < num_tracks: track = self.topLevelItem(i) i += 1 track.setText(0, str(i)) # 0 = index if user_sort: track.setText(4, str(i)) # 4 = user sort index self.playlist.sync(self, user_sort) # sync playlist to this view def dropEvent(self, event: QDropEvent): # receive items that were dropped and create new items from its tracks (new items bc. widgets can only have # one parent) if event.source() == self: items = self.selectedItems() # dragged items are always selected items self.itemDropped.emit(self, items) else: items = self.app.gui.dropped i = 0 for item in items: track = item.track self.playlist.tracks.append(track) track_item = TrackItem(track, i, self) i += 1 super().dropEvent(event) event.accept() self.on_sort(True) def dragEnterEvent(self, event): # store dragged items in gui.dropped, so the other playlist can receive it if event.source() == self: items = self.selectedItems() self.app.gui.dropped = items super().dragEnterEvent(event) event.accept() def load_tracks(self): i = 0 for track in self.playlist.tracks: track_item = TrackItem(track, i, self) i += 1 def on_track_activation(self, item, column): if not self.app.player.current_playlist == self.playlist: self.app.player.current_playlist = self.playlist index = self.indexOfTopLevelItem(item) self.app.player.play_track_in_playlist(index) def on_track_change(self, previous_track, track): # unmark the previous track and playlist and mark the current track and playlist as playing playlist_tabs = self.parent().parent() index = playlist_tabs.indexOf(self) # tab index of this playlist if previous_track: # unmark all playlists by looping through the tabs for i in range(playlist_tabs.count()): playlist_tabs.setTabIcon(i, QIcon(None)) # unmark the previous track in all playlists for item in previous_track.items: item.unmark() if track: playlist_tabs.setTabIcon(index, self.playing_mark) # mark this playlist # mark the current track in this playlist item = self.topLevelItem(self.app.player.current_playlist.current_track_index) item.mark() def append_track(self, track): TrackItem(track, self.topLevelItemCount(), self)