From 3dd9123332fb402a7379c5d68c621b042cc7d0b2 Mon Sep 17 00:00:00 2001 From: The Wobbler Date: Sun, 23 Feb 2025 16:38:56 +0100 Subject: [PATCH] Implemented sorting by track title, artist name etc... (Sorting order is not getting saved.) --- wobuzz/player/playlist.py | 41 ++++++++++++++++++++++-- wobuzz/ui/playlist.py | 67 ++++++++++++++++++++++++++++----------- wobuzz/ui/track.py | 13 +++++++- 3 files changed, 100 insertions(+), 21 deletions(-) diff --git a/wobuzz/player/playlist.py b/wobuzz/player/playlist.py index 60140ab..11f087c 100644 --- a/wobuzz/player/playlist.py +++ b/wobuzz/player/playlist.py @@ -22,7 +22,15 @@ class Playlist: # no other playlist can be created using the same name self.app.utils.unique_names.append(self.title) - self.sorting: list[Qt.SortOrder] | None = None # Custom sort order if None + # the number is the index of the header section, + # the bool is the sorting order (True = ascending, False = descending) + self.sorting: list[tuple[int, bool]] = [ + (0, True), + (1, True), + (2, True), + (3, True), + (4, True) + ] self.tracks: list[Track] = [] self.current_track_index = 0 self.current_track: Track | None = None @@ -127,6 +135,30 @@ class Playlist: def load_from_wbz(self, path): self.load_from_m3u(path) # placeholder + + def sync(self, view, user_sort: bool=False): + num_tracks = view.topLevelItemCount() + + i = 0 + + while i < num_tracks: + track_item = view.topLevelItem(i) + track = track_item.track + + track_item.index = i + + if user_sort: + track_item.index_user_sort = i + + self.tracks[i] = track + + track.set_occurrences() + + i += 1 + + # make sure the next track is cached (could be moved by user) + if self.app.player.current_playlist == self and self.has_tracks(): + self.app.player.cache_next_track() def has_tracks(self): return len(self.tracks) > 0 @@ -169,7 +201,12 @@ class Playlist: return self.current_track.sound, self.current_track.duration def save(self): - wbz_data = "" + + first_view = list(self.views.values())[0] + first_view.sortItems(4, Qt.SortOrder.AscendingOrder) + self.sync(first_view) + + wbz_data = "#WOBUZZM3U\n" for track in self.tracks: wbz_data += f"{track.path}\n" diff --git a/wobuzz/ui/playlist.py b/wobuzz/ui/playlist.py index 03892b4..9be7fb2 100644 --- a/wobuzz/ui/playlist.py +++ b/wobuzz/ui/playlist.py @@ -1,7 +1,7 @@ #!/usr/bin/python3 -from PyQt6.QtCore import pyqtSignal -from PyQt6.QtGui import QDropEvent, QIcon, QFont +from PyQt6.QtCore import Qt, pyqtSignal +from PyQt6.QtGui import QDropEvent, QIcon from PyQt6.QtWidgets import QTreeWidget, QAbstractItemView from .track import TrackItem @@ -20,6 +20,9 @@ class PlaylistView(QTreeWidget): self.app = playlist.app + self.header = self.header() + self.header.setSectionsClickable(True) + playlist.views[id(dock)] = self self.setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection) @@ -37,30 +40,58 @@ class PlaylistView(QTreeWidget): self.setHeaderLabels(headers) self.itemActivated.connect(self.on_track_activation) + self.header.sectionClicked.connect(self.on_header_click) - def on_user_sort(self): + 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 + + 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_item = self.topLevelItem(i) - track = track_item.track - - track_item.index_user_sort = i - track_item.index = i - - track_item.setText(5, str(i + 1)) - - self.playlist.tracks[i] = track - - track.set_occurrences() + track = self.topLevelItem(i) i += 1 - # make sure the next track is cached (could be moved by user) - if self.app.player.current_playlist == self.playlist and self.app.player.current_playlist.has_tracks(): - self.app.player.cache_next_track() + 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 @@ -88,7 +119,7 @@ class PlaylistView(QTreeWidget): event.accept() - self.on_user_sort() + self.on_sort(True) def dragEnterEvent(self, event): # store dragged items in gui.dropped, so the other playlist can receive it diff --git a/wobuzz/ui/track.py b/wobuzz/ui/track.py index 80fa6ee..e1c0f24 100644 --- a/wobuzz/ui/track.py +++ b/wobuzz/ui/track.py @@ -18,6 +18,7 @@ class TrackItem(QTreeWidgetItem): self.track = track self.index_user_sort = index self.index = index + self.parent = parent self.playlist = parent.playlist @@ -48,10 +49,20 @@ class TrackItem(QTreeWidgetItem): self.setFont(2, self.bold_font) self.setFont(3, self.normal_font) - def unmark(self): self.setIcon(0, QIcon(None)) self.setFont(1, self.normal_font) self.setFont(2, self.normal_font) self.setFont(3, self.normal_font) + def __lt__(self, other): + # make numeric strings get sorted the right way + + column = self.parent.sortColumn() + + if column == 0 or column == 4: + return int(self.text(column)) < int(other.text(column)) + + else: + return super().__lt__(other) +