Add sort selection
This commit is contained in:
parent
ee391e42e7
commit
8e2edb6af3
115
app/playlists.py
115
app/playlists.py
@ -270,13 +270,22 @@ class PlaylistTab(QTableWidget):
|
||||
self.hide_or_show_played_tracks()
|
||||
|
||||
def _add_context_menu(
|
||||
self, text: str, action: Callable, disabled: bool = False
|
||||
) -> QAction:
|
||||
self,
|
||||
text: str,
|
||||
action: Callable,
|
||||
disabled: bool = False,
|
||||
parent_menu: Optional[QMenu] = None,
|
||||
) -> Optional[QAction]:
|
||||
"""
|
||||
Add item to self.menu
|
||||
"""
|
||||
|
||||
menu_item = self.menu.addAction(text)
|
||||
if parent_menu is None:
|
||||
parent_menu = self.menu
|
||||
|
||||
menu_item = parent_menu.addAction(text)
|
||||
if not menu_item:
|
||||
return None
|
||||
menu_item.setDisabled(disabled)
|
||||
menu_item.triggered.connect(action)
|
||||
|
||||
@ -1073,6 +1082,30 @@ class PlaylistTab(QTableWidget):
|
||||
# ----------------------
|
||||
self.menu.addSeparator()
|
||||
|
||||
# Sort
|
||||
sort_menu = self.menu.addMenu("Sort")
|
||||
self._add_context_menu(
|
||||
"by title", lambda: self._sort_selection(TITLE), parent_menu=sort_menu
|
||||
)
|
||||
self._add_context_menu(
|
||||
"by artist", lambda: self._sort_selection(ARTIST), parent_menu=sort_menu
|
||||
)
|
||||
self._add_context_menu(
|
||||
"by duration", lambda: self._sort_selection(DURATION), parent_menu=sort_menu
|
||||
)
|
||||
self._add_context_menu(
|
||||
"by last played",
|
||||
lambda: self._sort_selection(LASTPLAYED),
|
||||
parent_menu=sort_menu,
|
||||
)
|
||||
if sort_menu:
|
||||
sort_menu.setEnabled(self._sortable())
|
||||
|
||||
# Build submenu
|
||||
|
||||
# ----------------------
|
||||
self.menu.addSeparator()
|
||||
|
||||
# Info
|
||||
if track_row:
|
||||
self._add_context_menu("Info", lambda: self._info_row(track_id))
|
||||
@ -2184,6 +2217,82 @@ class PlaylistTab(QTableWidget):
|
||||
|
||||
return item
|
||||
|
||||
def _sortable(self) -> bool:
|
||||
"""
|
||||
Return True if the selection is sortable. That means:
|
||||
- at least two rows selected
|
||||
- selected rows are contiguous
|
||||
- selected rows do not include any header rows
|
||||
"""
|
||||
|
||||
selectionModel = self.selectionModel()
|
||||
if not selectionModel:
|
||||
return False
|
||||
source_rows = selectionModel.selectedRows()
|
||||
sorted_source_rows = sorted([a.row() for a in source_rows])
|
||||
if len(sorted_source_rows) < 2:
|
||||
return False
|
||||
|
||||
if sorted_source_rows != list(
|
||||
range(min(sorted_source_rows), max(sorted_source_rows) + 1)
|
||||
):
|
||||
return False
|
||||
|
||||
for row in sorted_source_rows:
|
||||
if self._get_row_track_id(row) == 0:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def _sort_selection(self, sort_column: int) -> None:
|
||||
"""
|
||||
Algorithm:
|
||||
- check row selection is contiguous; return if not
|
||||
- copy (row-number, sort-field) to a list
|
||||
- sort the list by sort-field
|
||||
- create a new row after the selection
|
||||
- iterate the list and move items to new row
|
||||
- create another new row and repeat until all rows moved
|
||||
- delete old rows
|
||||
"""
|
||||
|
||||
# Check selection is contiguous
|
||||
selectionModel = self.selectionModel()
|
||||
if not selectionModel:
|
||||
return
|
||||
source_rows = selectionModel.selectedRows()
|
||||
if not self._sortable():
|
||||
return
|
||||
|
||||
# Copy (row-number, sort-field) to a list
|
||||
sorted_rows = []
|
||||
for index in source_rows:
|
||||
sort_item = self.item(index.row(), sort_column)
|
||||
if sort_item:
|
||||
sorted_rows.append((index.row(), sort_item.text()))
|
||||
print(f"{sort_item.text()=}")
|
||||
|
||||
# Sort the list
|
||||
sorted_rows.sort(key=lambda row: row[1])
|
||||
|
||||
# Move rows
|
||||
source_row_numbers = [a[0] for a in sorted_rows]
|
||||
next_row = max(source_row_numbers) + 1
|
||||
for source_row_number in source_row_numbers:
|
||||
self.insertRow(next_row)
|
||||
for column in range(self.columnCount()):
|
||||
self.setItem(next_row, column, self.takeItem(source_row_number, column))
|
||||
next_row += 1
|
||||
|
||||
# Remove source rows
|
||||
for i in reversed(source_rows):
|
||||
self.removeRow(i.row())
|
||||
|
||||
# Save playlist
|
||||
# with Session() as session:
|
||||
# self.save_playlist(session)
|
||||
# self._update_start_end_times(session)
|
||||
|
||||
def _track_time_between_rows(
|
||||
self, session: scoped_session, from_plr: PlaylistRows, to_plr: PlaylistRows
|
||||
) -> int:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user