Compare commits

...

2 Commits

Author SHA1 Message Date
Keith Edmunds
558554d086 Implement "remove comments"
Fixes #185
2024-12-09 08:45:41 +00:00
Keith Edmunds
417bff8663 Put mark/move on context menu 2024-12-08 22:36:05 +00:00
3 changed files with 96 additions and 20 deletions

View File

@ -461,7 +461,7 @@ class Window(QMainWindow, Ui_MainWindow):
self.actionImport.triggered.connect(self.import_track)
self.actionInsertSectionHeader.triggered.connect(self.insert_header)
self.actionInsertTrack.triggered.connect(self.insert_track)
self.actionMark_for_moving.triggered.connect(self.cut_rows)
self.actionMark_for_moving.triggered.connect(self.mark_rows_for_moving)
self.actionMoveSelected.triggered.connect(self.move_selected)
self.actionNew_from_template.triggered.connect(self.new_from_template)
self.actionNewPlaylist.triggered.connect(self.create_and_show_playlist)
@ -565,18 +565,6 @@ class Window(QMainWindow, Ui_MainWindow):
log.debug(f"create_playlist_tab() returned: {idx=}")
return idx
def cut_rows(self) -> None:
"""
Cut rows ready for pasting.
"""
# Save the selected PlaylistRows items ready for a later
# paste
self.move_source_rows = self.active_tab().get_selected_rows()
self.move_source_model = self.active_proxy_model()
log.debug(f"cut_rows(): {self.move_source_rows=} {self.move_source_model=}")
def debug(self):
"""Invoke debugger"""
@ -930,6 +918,18 @@ class Window(QMainWindow, Ui_MainWindow):
self.signals.search_wikipedia_signal.emit(track_info.title)
def mark_rows_for_moving(self) -> None:
"""
Cut rows ready for pasting.
"""
# Save the selected PlaylistRows items ready for a later
# paste
self.move_source_rows = self.active_tab().get_selected_rows()
self.move_source_model = self.active_proxy_model()
log.debug(f"mark_rows_for_moving(): {self.move_source_rows=} {self.move_source_model=}")
def move_playlist_rows(self, row_numbers: List[int]) -> None:
"""
Move passed playlist rows to another playlist

View File

@ -40,6 +40,7 @@ from classes import (
)
from config import Config
from helpers import (
ask_yes_no,
file_is_unreadable,
get_embedded_time,
get_relative_date,
@ -67,7 +68,7 @@ class PlaylistModel(QAbstractTableModel):
database
refresh_data() will repopulate all of playlist_rows from the
database
database.
"""
def __init__(
@ -993,10 +994,17 @@ class PlaylistModel(QAbstractTableModel):
@line_profiler.profile
def refresh_data(self, session: db.session, dummy_for_profiling=None) -> None:
"""Populate self.playlist_rows with playlist data"""
"""
Populate self.playlist_rows with playlist data
We used to clear self.playlist_rows each time but that's
expensive and slow on big playlists. Instead we track where rows
are in database versus self.playlist_rows and fixup the latter.
This works well for news rows added and for rows moved, but
doesn't work for changed comments so they must be handled using
refresh_row().
"""
# We used to clear self.playlist_rows each time but that's
# expensive and slow on big playlists
# Note where each playlist_id is
plid_to_row: dict[int, int] = {}
@ -1016,12 +1024,13 @@ class PlaylistModel(QAbstractTableModel):
# Copy to self.playlist_rows
self.playlist_rows = new_playlist_rows
# Same as refresh data, but only used when creating playslit.
# Distinguishes profile time between initial load and other
# refreshes.
def load_data(self, session: db.session, dummy_for_profiling=None) -> None:
"""Populate self.playlist_rows with playlist data"""
# Same as refresh data, but only used when creating playslit.
# Distinguishes profile time between initial load and other
# refreshes.
# We used to clear self.playlist_rows each time but that's
# expensive and slow on big playlists
@ -1107,6 +1116,38 @@ class PlaylistModel(QAbstractTableModel):
self.update_track_times()
def remove_comments(self, row_numbers: list[int]) -> None:
"""
Remove comments from passed rows
"""
if not row_numbers:
return
# Safety check
if not ask_yes_no(
title="Remove comments",
question=f"Remove comments from {len(row_numbers)} rows?"
):
return
with db.Session() as session:
for row_number in row_numbers:
playlist_row = session.get(
PlaylistRows, self.playlist_rows[row_number].playlistrow_id
)
if playlist_row.track_id:
playlist_row.note = ""
# We can't use refresh_data() because its
# optimisations mean it won't update comments in
# self.playlist_rows
# The "correct" approach would be to re-read from the
# database but we optimise here by simply updating
# self.playlist_rows directly.
self.playlist_rows[row_number].note = ""
session.commit()
self.invalidate_rows(row_numbers)
def _reversed_contiguous_row_groups(
self, row_numbers: list[int]
) -> list[list[int]]:

View File

@ -483,6 +483,13 @@ class PlaylistTab(QTableView):
self._add_context_menu(
"Rescan track", lambda: self._rescan(model_row_number)
)
self._add_context_menu(
"Mark for moving", lambda: self._mark_for_moving()
)
if self.musicmuster.move_source_rows:
self._add_context_menu(
"Move selected rows here", lambda: self._move_selected_rows()
)
# ----------------------
self.menu.addSeparator()
@ -498,6 +505,9 @@ class PlaylistTab(QTableView):
lambda: proxy_model.remove_track(model_row_number),
)
# Remove comments
self._add_context_menu("Remove comments", lambda: self._remove_comments())
# Add track to section header (ie, make this a track row)
if header_row:
self._add_context_menu("Add a track", lambda: self._add_track())
@ -740,6 +750,20 @@ class PlaylistTab(QTableView):
self.source_model.mark_unplayed(row_numbers)
self.clear_selection()
def _mark_for_moving(self) -> None:
"""
Mark selected rows for pasting
"""
self.musicmuster.mark_rows_for_moving()
def _move_selected_rows(self) -> None:
"""
Move selected rows here
"""
self.musicmuster.paste_rows()
def _open_in_audacity(self, row_number: int) -> None:
"""
Open track in passed row in Audacity
@ -757,6 +781,17 @@ class PlaylistTab(QTableView):
except ApplicationError as e:
show_warning(self.musicmuster, "Audacity error", str(e))
def _remove_comments(self) -> None:
"""
Remove comments from selected rows
"""
row_numbers = self.selected_model_row_numbers()
if not row_numbers:
return
self.source_model.remove_comments(row_numbers)
def _rescan(self, row_number: int) -> None:
"""Rescan track"""