Add sort selection

This commit is contained in:
Keith Edmunds 2023-10-10 01:27:36 +01:00
parent ee391e42e7
commit 8e2edb6af3

View File

@ -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: