Compare commits

..

No commits in common. "c58eb47cc1b706abe70765c883ac483fbe1a0f88" and "25771f52356a563cc011202d88cc4283c42db38b" have entirely different histories.

6 changed files with 92 additions and 161 deletions

View File

@ -247,6 +247,11 @@ class MusicMusterSignals(QObject):
# Signals that the playlist_id passed should resize all rows.
resize_rows_signal = pyqtSignal(int)
# Signal to open browser at songfacts or wikipedia page matching
# passed string.
search_songfacts_signal = pyqtSignal(str)
search_wikipedia_signal = pyqtSignal(str)
# Displays a warning dialog
show_warning_signal = pyqtSignal(str, str)
@ -279,14 +284,14 @@ class MusicMusterSignals(QObject):
# Emited when a track starts playing
signal_track_started = pyqtSignal()
# Emitted when track ends or is manually faded
signal_track_ended = pyqtSignal(int)
# Used by model to signal spanning of cells to playlist for headers
span_cells_signal = pyqtSignal(int, int, int, int, int)
# Dispay status message to user
status_message_signal = pyqtSignal(str, int)
# Emitted when track ends or is manually faded
signal_track_ended = pyqtSignal(int)
def __post_init__(self):
super().__init__()

View File

@ -101,7 +101,7 @@ class Music:
if not self.player.get_position() > 0 and self.player.is_playing():
return
self.emit_signal_track_ended()
self.signal_track_ended()
self.fader_worker = _FadeTrack(self.player, fade_seconds=fade_seconds)
self.fader_worker.finished.connect(self.player.release)
@ -225,7 +225,7 @@ class Music:
log.debug(f"Volume reset from {volume=}")
sleep(0.1)
def emit_signal_track_ended(self) -> None:
def signal_track_ended(self) -> None:
"""
Multiple parts of the Music class can signal that the track has
ended. Handle them all here to ensure that only one such signal
@ -254,7 +254,7 @@ class Music:
self.player.stop()
self.player.release()
self.player = None
self.emit_signal_track_ended()
self.signal_track_ended()
def track_end_event_handler(self, event: vlc.Event) -> None:
"""
@ -262,4 +262,4 @@ class Music:
"""
log.debug("track_end_event_handler() called")
self.emit_signal_track_ended()
self.signal_track_ended()

View File

@ -6,7 +6,7 @@ from functools import partial
from slugify import slugify # type: ignore
from typing import Any, Callable
import argparse
from dataclasses import dataclass, field
from dataclasses import dataclass
import datetime as dt
import os
import subprocess
@ -70,14 +70,14 @@ from classes import (
MusicMusterSignals,
PlaylistDTO,
QueryDTO,
SelectedRows,
TrackAndPlaylist,
TrackInfo,
)
from config import Config
from dialogs import TrackInsertDialog
from file_importer import FileImporter
from helpers import file_is_unreadable, get_name
from helpers import ask_yes_no, file_is_unreadable, get_name
from log import log, log_call
from playlistmodel import PlaylistModel, PlaylistProxyModel
from playlistrow import PlaylistRow, TrackSequence
@ -104,6 +104,15 @@ class SignalMonitor:
self.signals.resize_rows_signal.connect(
partial(self.show_signal, "resize_rows_signal ")
)
self.signals.search_songfacts_signal.connect(
partial(self.show_signal, "search_songfacts_signal ")
)
self.signals.search_wikipedia_signal.connect(
partial(self.show_signal, "search_wikipedia_signal ")
)
self.signals.show_warning_signal.connect(
partial(self.show_signal, "show_warning_signal ")
)
self.signals.signal_add_track_to_header.connect(
partial(self.show_signal, "signal_add_track_to_header ")
)
@ -143,12 +152,17 @@ class SignalMonitor:
log.debug(f"{name=}, args={args}")
@dataclass
class Current:
base_model: PlaylistModel
proxy_model: PlaylistProxyModel
playlist_id: int
selected_row_numbers: list[int] = field(default_factory=list)
playlist_id: int = 0
selected_row_numbers: list[int] = []
def __repr__(self):
return (
f"<Current(base_model={self.base_model}, proxy_model={self.proxy_model}, "
f"playlist_id={self.playlist_id}, selected_rows={self.selected_row_numbers}>"
)
class DownloadCSV(QDialog):
@ -1211,7 +1225,7 @@ class Window(QMainWindow):
self.disable_selection_timing = False
self.catch_return_key = False
self.importer: FileImporter | None = None
self.current: Current | None = None
self.current = Current()
self.track_sequence = TrackSequence()
self.signals = MusicMusterSignals()
self.connect_signals_slots()
@ -1241,10 +1255,7 @@ class Window(QMainWindow):
return
# Don't allow window to close when a track is playing
if (
self.track_sequence.current
and self.track_sequence.current.music.is_playing()
):
if self.track_sequence.current and self.track_sequence.current.music.is_playing():
event.ignore()
helpers.show_warning(
self, "Track playing", "Can't close application while track is playing"
@ -1497,9 +1508,6 @@ class Window(QMainWindow):
but unused.
"""
if self.current is None:
return
playlist = ds.playlist_by_id(self.current.playlist_id)
if playlist:
if helpers.ask_yes_no(
@ -1524,9 +1532,6 @@ class Window(QMainWindow):
def save_as_template(self, checked: bool = False) -> None:
"""Save current playlist as template"""
if self.current is None:
return
template_names = [a.name for a in ds.playlists_templates_all()]
while True:
@ -1616,9 +1621,6 @@ class Window(QMainWindow):
Show query dialog with query_id selected
"""
if self.current is None:
return
# Keep a reference else it will be gc'd
self.query_dialog = QueryDialog(self.current.playlist_id, query_id)
self.query_dialog.exec()
@ -1736,13 +1738,14 @@ class Window(QMainWindow):
self.tabBar = self.playlist_section.tabPlaylist.tabBar()
self.txtSearch.textChanged.connect(self.search_playlist_text_changed)
self.signals.enable_escape_signal.connect(self.enable_escape_signal_handler)
self.signals.enable_escape_signal.connect(self.enable_escape)
self.signals.search_songfacts_signal.connect(self.open_songfacts_browser)
self.signals.search_wikipedia_signal.connect(self.open_wikipedia_browser)
self.signals.show_warning_signal.connect(self.show_warning)
self.signals.signal_next_track_changed.connect(self.next_track_changed_handler)
self.signals.signal_set_next_track.connect(self.set_next_track_handler)
self.signals.signal_next_track_changed.connect(self.signal_next_track_changed_handler)
self.signals.signal_set_next_track.connect(self.signal_set_next_track_handler)
self.signals.status_message_signal.connect(self.show_status_message)
self.signals.signal_track_ended.connect(self.track_ended_handler)
self.signals.signal_playlist_selected_rows.connect(self.playlist_selected_rows_handler)
self.signals.signal_track_ended.connect(self.end_of_track_actions)
self.timer10.timeout.connect(self.tick_10ms)
self.timer500.timeout.connect(self.tick_500ms)
@ -1759,13 +1762,8 @@ class Window(QMainWindow):
# TODO should be able to have the model handle row depending on
# how current_row_or_end is used
if self.current is None:
return 0 # hack, but should never be called without self.current set
if self.current.selected_row_numbers:
return self.current.selected_row_numbers[0]
if not self.current.base_model:
return 0 # hack, but mostly there WILL be a current model
return self.current.base_model.rowCount()
def debug(self, checked: bool = False) -> None:
@ -1808,7 +1806,7 @@ class Window(QMainWindow):
)
# @log_call
def enable_escape_signal_handler(self, enabled: bool) -> None:
def enable_escape(self, enabled: bool) -> None:
"""
Manage signal to enable/disable handling ESC character.
@ -1820,7 +1818,7 @@ class Window(QMainWindow):
self.menu_actions["clear_selection"].setEnabled(enabled)
# @log_call
def track_ended_handler(self) -> None:
def end_of_track_actions(self) -> None:
"""
Called by signal_track_ended
@ -1851,9 +1849,6 @@ class Window(QMainWindow):
def export_playlist_tab(self, checked: bool = False) -> None:
"""Export the current playlist to an m3u file"""
if self.current is None:
return
playlist_id = self.current.playlist_id
playlist = ds.playlist_by_id(playlist_id)
@ -1926,7 +1921,7 @@ class Window(QMainWindow):
self.current.base_model.hide_played_tracks(True)
# Reset row heights
self.signals.resize_rows_signal.emit(self.current.playlist_id)
self._active_tab().resize_rows()
def import_files_wrapper(self, checked: bool = False) -> None:
"""
@ -1935,18 +1930,12 @@ class Window(QMainWindow):
# We need to keep a reference to the FileImporter else it will be
# garbage collected while import threads are still running
if self.current is None:
return
self.importer = FileImporter(self.current.base_model, self.current_row_or_end())
self.importer.start()
def insert_header(self, checked: bool = False) -> None:
"""Show dialog box to enter header text and add to playlist"""
if self.current is None:
return
# Get header text
dlg: QInputDialog = QInputDialog(self)
dlg.setInputMode(QInputDialog.InputMode.TextInput)
@ -1965,9 +1954,6 @@ class Window(QMainWindow):
def insert_track(self, checked: bool = False) -> None:
"""Show dialog box to select and add track from database"""
if self.current is None:
return
dlg = TrackInsertDialog(parent=self, playlist_id=self.current.playlist_id)
dlg.exec()
@ -1995,7 +1981,7 @@ class Window(QMainWindow):
if not track_info:
return
self.open_songfacts_browser(track_info.title)
self.signals.search_songfacts_signal.emit(track_info.title)
def lookup_row_in_wikipedia(self, checked: bool = False) -> None:
"""
@ -2006,16 +1992,13 @@ class Window(QMainWindow):
if not track_info:
return
self.open_wikipedia_browser(track_info.title)
self.signals.search_wikipedia_signal.emit(track_info.title)
def mark_rows_for_moving(self, checked: bool = False) -> None:
"""
Cut rows ready for pasting.
"""
if self.current is None:
return
# Save the selected PlaylistRows items ready for a later
# paste
self.move_source = MoveSource(
@ -2030,7 +2013,7 @@ class Window(QMainWindow):
Move passed playlist rows to another playlist
"""
if not row_numbers or self.current is None:
if not row_numbers:
return
# Identify destination playlist
@ -2066,9 +2049,6 @@ class Window(QMainWindow):
Move selected rows to another playlist
"""
if self.current is None:
return
self.move_playlist_rows(self.current.selected_row_numbers)
def move_unplayed(self, checked: bool = False) -> None:
@ -2076,9 +2056,6 @@ class Window(QMainWindow):
Move unplayed rows to another playlist
"""
if self.current is None:
return
unplayed_rows = self.current.base_model.get_unplayed_rows()
if not unplayed_rows:
return
@ -2114,7 +2091,7 @@ class Window(QMainWindow):
'checked' is a dummy parameter passed to us by the menu
"""
if not self.move_source or self.current is None:
if not self.move_source:
return
to_playlist_model = self.current.base_model
@ -2129,7 +2106,7 @@ class Window(QMainWindow):
from_rows, to_row, to_playlist_model.playlist_id
)
self.signals.resize_rows_signal.emit(self.current.playlist_id)
self._active_tab().resize_rows()
self._active_tab().clear_selection()
# If we move a row to immediately under the current track, make
@ -2139,7 +2116,7 @@ class Window(QMainWindow):
and self.track_sequence.current.playlist_id == to_playlist_model.playlist_id
and to_row == self.track_sequence.current.row_number + 1
):
to_playlist_model.set_next_row_handler(to_row)
to_playlist_model.set_next_row(to_row)
# @log_call
def play_next(self, position: float | None = None, checked: bool = False) -> None:
@ -2182,7 +2159,7 @@ class Window(QMainWindow):
if self.track_sequence.current:
self.track_sequence.current.fade()
# Move next track to current track. signal_track_ended_handler() will
# Move next track to current track. end_of_track_actions() will
# have been called when previous track ended or when fade() was
# called above, and that in turn will have saved current track to
# previous_track
@ -2290,9 +2267,6 @@ class Window(QMainWindow):
def preview_mark(self) -> None:
"""Set intro time"""
if self.current is None:
return
if self.preview_manager.is_playing():
track_id = self.preview_manager.track_id
row_number = self.preview_manager.row_number
@ -2334,9 +2308,6 @@ class Window(QMainWindow):
Rename current playlist. checked is passed by menu but not used here
"""
if self.current is None:
return
playlist = ds.playlist_by_id(self.current.playlist_id)
if playlist:
new_name = self.get_playlist_name(playlist.name)
@ -2454,9 +2425,6 @@ class Window(QMainWindow):
Incremental search of playlist
"""
if self.current is None:
return
self.current.proxy_model.set_incremental_search(self.txtSearch.text())
def selected_or_next_track_info(self) -> PlaylistRow | None:
@ -2465,9 +2433,6 @@ class Window(QMainWindow):
next track. If no next track, return None.
"""
if self.current is None:
return None
row_number: int | None = None
if self.current.selected_row_numbers:
@ -2500,9 +2465,6 @@ class Window(QMainWindow):
Set currently-selected row on visible playlist tab as next track
"""
if self.current is None:
return
self.signals.signal_set_next_row.emit(self.current.playlist_id)
self.clear_selection()
@ -2541,9 +2503,6 @@ class Window(QMainWindow):
def show_track(self, playlist_track: PlaylistRow) -> None:
"""Scroll to show track"""
if self.current is None:
return
# Switch to the correct tab
playlist_id = playlist_track.playlist_id
if not playlist_id:
@ -2562,20 +2521,7 @@ class Window(QMainWindow):
self._active_tab().scroll_to_top(playlist_track.row_number)
def playlist_selected_rows_handler(
self, selected_rows: SelectedRows
) -> None:
"""
Handle signal_playlist_selected_rows to keep track of which rows
are selected in the current model
"""
if self.current is None:
return
self.current.selected_row_numbers = selected_rows.rows
def set_next_track_handler(self, plr: PlaylistRow) -> None:
def signal_set_next_track_handler(self, plr: PlaylistRow) -> None:
"""
Handle signal_set_next_track
"""
@ -2583,7 +2529,7 @@ class Window(QMainWindow):
self.track_sequence.set_next(plr)
self.signals.signal_next_track_changed.emit()
def next_track_changed_handler(self) -> None:
def signal_next_track_changed_handler(self) -> None:
"""
Handle next track changed
"""
@ -2683,10 +2629,7 @@ class Window(QMainWindow):
"""
# If track is playing, update track clocks time and colours
if (
self.track_sequence.current
and self.track_sequence.current.music.is_playing()
):
if self.track_sequence.current and self.track_sequence.current.music.is_playing():
# Elapsed time
self.header_section.label_elapsed_timer.setText(
helpers.ms_to_mmss(self.track_sequence.current.time_playing())
@ -2741,24 +2684,6 @@ class Window(QMainWindow):
helpers.ms_to_mmss(time_to_silence)
)
def update_current(
self,
base_model: PlaylistModel,
proxy_model: PlaylistProxyModel,
playlist_id: int,
selected_row_numbers: list[int]
) -> None:
"""
Update self.current when playlist tab changes. Called by new playlist
"""
self.current = Current(
base_model=base_model,
proxy_model=proxy_model,
playlist_id=playlist_id,
selected_row_numbers=selected_row_numbers
)
def update_headers(self) -> None:
"""
Update last / current / next track headers

View File

@ -93,15 +93,17 @@ class PlaylistModel(QAbstractTableModel):
self.played_tracks_hidden = False
# Connect signals
self.signals.signal_add_track_to_header.connect(self.signal_add_track_to_header_handler)
self.signals.signal_begin_insert_rows.connect(self.begin_insert_rows_handler)
self.signals.signal_end_insert_rows.connect(self.end_insert_rows_handler)
self.signals.signal_add_track_to_header.connect(self.add_track_to_header)
self.signals.signal_begin_insert_rows.connect(self.begin_insert_rows)
self.signals.signal_end_insert_rows.connect(self.end_insert_rows)
self.signals.signal_insert_track.connect(self.insert_row_signal_handler)
self.signals.signal_playlist_selected_rows.connect(self.playlist_selected_rows_handler)
self.signals.signal_set_next_row.connect(self.set_next_row_handler)
self.signals.signal_track_started.connect(self.track_started_handler)
self.signals.signal_track_ended.connect(self.signal_track_ended_handler)
self.signals.signal_next_track_changed.connect(self.next_track_changed_handler)
self.signals.signal_playlist_selected_rows.connect(self.set_selected_rows)
self.signals.signal_set_next_row.connect(self.set_next_row)
self.signals.signal_track_started.connect(self.track_started)
self.signals.signal_track_ended.connect(self.previous_track_ended)
self.signals.signal_next_track_changed.connect(
self.signal_next_track_changed_handler
)
# Populate self.playlist_rows
for dto in ds.playlistrows_by_playlist(self.playlist_id):
@ -150,7 +152,7 @@ class PlaylistModel(QAbstractTableModel):
return header_row
# @log_call
def signal_add_track_to_header_handler(self, track_and_playlist: TrackAndPlaylist) -> None:
def add_track_to_header(self, track_and_playlist: TrackAndPlaylist) -> None:
"""
Handle signal_add_track_to_header
"""
@ -249,7 +251,7 @@ class PlaylistModel(QAbstractTableModel):
return len(Col)
# @log_call
def track_started_handler(self) -> None:
def track_started(self) -> None:
"""
Handle signal_track_started signal.
@ -894,7 +896,7 @@ class PlaylistModel(QAbstractTableModel):
self.track_sequence.update()
self.update_track_times()
def begin_insert_rows_handler(self, insert_rows: InsertRows) -> None:
def begin_insert_rows(self, insert_rows: InsertRows) -> None:
"""
Prepare model to insert rows
"""
@ -904,7 +906,7 @@ class PlaylistModel(QAbstractTableModel):
super().beginInsertRows(QModelIndex(), insert_rows.from_row, insert_rows.to_row)
def end_insert_rows_handler(self, playlist_id: int) -> None:
def end_insert_rows(self, playlist_id: int) -> None:
"""
End insert rows
"""
@ -970,7 +972,7 @@ class PlaylistModel(QAbstractTableModel):
return
# @log_call
def signal_track_ended_handler(self, playlist_id: int) -> None:
def previous_track_ended(self, playlist_id: int) -> None:
"""
Notification from signal_track_ended that the previous track has ended.
@ -986,12 +988,12 @@ class PlaylistModel(QAbstractTableModel):
# Sanity check
if not self.track_sequence.previous:
log.error(
f"{self}: playlistmodel:signal_track_ended_handler called with no current track"
f"{self}: playlistmodel:previous_track_ended called with no current track"
)
return
if self.track_sequence.previous.row_number is None:
log.error(
f"{self}: signal_track_ended_handler called with no row number "
f"{self}: previous_track_ended called with no row number "
f"({self.track_sequence.previous=})"
)
return
@ -1253,7 +1255,7 @@ class PlaylistModel(QAbstractTableModel):
return True
# @log_call
def playlist_selected_rows_handler(self, selected_rows: SelectedRows) -> None:
def set_selected_rows(self, selected_rows: SelectedRows) -> None:
"""
Handle signal_playlist_selected_rows to keep track of which rows
are selected in the view
@ -1265,7 +1267,7 @@ class PlaylistModel(QAbstractTableModel):
self.selected_rows = [self.playlist_rows[a] for a in selected_rows.rows]
# @log_call
def set_next_row_handler(self, playlist_id: int) -> None:
def set_next_row(self, playlist_id: int) -> None:
"""
Handle signal_set_next_row
"""
@ -1304,7 +1306,7 @@ class PlaylistModel(QAbstractTableModel):
self.signals.signal_set_next_track.emit(plr)
def next_track_changed_handler(self) -> None:
def signal_next_track_changed_handler(self) -> None:
"""
Handle next track changed
"""

View File

@ -41,6 +41,7 @@ from classes import (
MusicMusterSignals,
PlaylistStyle,
SelectedRows,
TrackAndPlaylist,
TrackInfo
)
from config import Config
@ -309,9 +310,9 @@ class PlaylistTab(QTableView):
# Connect signals
self.signals = MusicMusterSignals()
self.signals.resize_rows_signal.connect(self.resize_rows_handler)
self.signals.span_cells_signal.connect(self._span_cells_handler)
self.signals.signal_track_started.connect(self.track_started_handler)
self.signals.resize_rows_signal.connect(self.resize_rows)
self.signals.span_cells_signal.connect(self._span_cells)
self.signals.signal_track_started.connect(self.track_started)
# Selection model
self.setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection)
@ -342,7 +343,7 @@ class PlaylistTab(QTableView):
v_header.sectionHandleDoubleClicked.connect(self.resizeRowToContents)
# Setting ResizeToContents causes screen flash on load
self.resize_rows_handler()
self.resize_rows()
# ########## Overridden class functions ##########
@ -353,12 +354,12 @@ class PlaylistTab(QTableView):
Override closeEditor to enable play controls and update display.
"""
self.signals.enable_escape_signal.emit(True)
self.musicmuster.enable_escape(True)
super(PlaylistTab, self).closeEditor(editor, hint)
# Optimise row heights after increasing row height for editing
self.resize_rows_handler()
self.resize_rows()
# Update start times in case a start time in a note has been
# edited
@ -429,11 +430,11 @@ class PlaylistTab(QTableView):
self.clear_selection()
# Resize rows
self.resize_rows_handler()
self.resize_rows()
# Set next row if we are immediately under current row
if set_next_row:
self.get_base_model().set_next_row_handler(set_next_row)
self.get_base_model().set_next_row(set_next_row)
event.accept()
@ -728,7 +729,7 @@ class PlaylistTab(QTableView):
cb.setText(track_path, mode=cb.Mode.Clipboard)
# @log_call
def track_started_handler(self) -> None:
def track_started(self) -> None:
"""
Called when track starts playing
"""
@ -964,7 +965,7 @@ class PlaylistTab(QTableView):
self.get_base_model().rescan_track(row_number)
self.clear_selection()
def resize_rows_handler(self, playlist_id: Optional[int] = None) -> None:
def resize_rows(self, playlist_id: Optional[int] = None) -> None:
"""
If playlist_id is us, resize rows
"""
@ -1091,10 +1092,10 @@ class PlaylistTab(QTableView):
log.debug(f"set_row_as_next_track() {model_row_number=}")
if model_row_number is None:
return
self.get_base_model().set_next_row_handler(model_row_number)
self.get_base_model().set_next_row(model_row_number)
self.clearSelection()
def _span_cells_handler(
def _span_cells(
self, playlist_id: int, row: int, column: int, rowSpan: int, columnSpan: int
) -> None:
"""
@ -1130,14 +1131,12 @@ class PlaylistTab(QTableView):
"""
# Update musicmuster
self.musicmuster.update_current(
base_model=self.get_base_model(),
proxy_model=self.model(),
playlist_id=self.playlist_id,
selected_row_numbers=self.get_selected_rows(),
)
self.musicmuster.current.playlist_id = self.playlist_id
self.musicmuster.current.selected_row_numbers = self.get_selected_rows()
self.musicmuster.current.base_model = self.get_base_model()
self.musicmuster.current.proxy_model = self.model()
self.resize_rows_handler()
self.resize_rows()
def _unmark_as_next(self) -> None:
"""Rescan track"""

View File

@ -225,7 +225,7 @@ class TestMMMiscRowMove(unittest.TestCase):
self.model.selected_rows = [self.model.playlist_rows[insert_row]]
prd = self.model.playlist_rows[1]
self.model.signal_add_track_to_header_handler(
self.model.add_track_to_header(
TrackAndPlaylist(playlist_id=self.model.playlist_id, track_id=prd.track_id)
)