diff --git a/app/datastructures.py b/app/datastructures.py new file mode 100644 index 0000000..6447dc4 --- /dev/null +++ b/app/datastructures.py @@ -0,0 +1,20 @@ +from PyQt6.QtCore import pyqtSignal, QObject + +from helpers import singleton + + +@singleton +class MusicMusterSignals(QObject): + """ + Class for all MusicMuster signals. See: + - https://zetcode.com/gui/pyqt5/eventssignals/ + - https://stackoverflow.com/questions/62654525/ + emit-a-signal-from-another-class-to-main-class + and Singleton class at + https://refactoring.guru/design-patterns/singleton/python/example#example-0 + """ + + enable_escape_signal = pyqtSignal(bool) + set_next_track_signal = pyqtSignal(int, int) + span_cells_signal = pyqtSignal(int, int, int, int) + add_track_to_playlist_signal = pyqtSignal(int, int, int, str) diff --git a/app/helpers.py b/app/helpers.py index 80115d9..2f1a0cc 100644 --- a/app/helpers.py +++ b/app/helpers.py @@ -1,3 +1,4 @@ +import functools import os import psutil import shutil @@ -381,6 +382,22 @@ def show_warning(parent: QMainWindow, title: str, msg: str) -> None: QMessageBox.warning(parent, title, msg, buttons=QMessageBox.StandardButton.Cancel) +def singleton(cls): + """ + Make a class a Singleton class (see + https://realpython.com/primer-on-python-decorators/#creating-singletons) + """ + + @functools.wraps(cls) + def wrapper_singleton(*args, **kwargs): + if not wrapper_singleton.instance: + wrapper_singleton.instance = cls(*args, **kwargs) + return wrapper_singleton.instance + + wrapper_singleton.instance = None + return wrapper_singleton + + def trailing_silence( audio_segment: AudioSegment, silence_threshold: int = -50, diff --git a/app/musicmuster.py b/app/musicmuster.py index cac1a2a..f65024e 100755 --- a/app/musicmuster.py +++ b/app/musicmuster.py @@ -69,6 +69,7 @@ import icons_rc # noqa F401 import music from models import Base, Carts, Playdates, PlaylistRows, Playlists, Settings, Tracks from config import Config +from datastructures import MusicMusterSignals from playlists import PlaylistTab from ui.dlg_cart_ui import Ui_DialogCartEdit # type: ignore from ui.dlg_TrackSelect_ui import Ui_Dialog # type: ignore @@ -238,20 +239,6 @@ class ImportTrack(QObject): self.finished.emit(self.playlist) -class MusicMusterSignals(QObject): - """ - Class for all MusicMuster signals. See: - - https://zetcode.com/gui/pyqt5/eventssignals/ - - https://stackoverflow.com/questions/62654525/ - emit-a-signal-from-another-class-to-main-class - """ - - enable_escape_signal = pyqtSignal(bool) - set_next_track_signal = pyqtSignal(int, int) - span_cells_signal = pyqtSignal(int, int, int, int) - add_track_to_playlist_signal = pyqtSignal(int, int, str) - - class PlaylistTrack: """ Used to provide a single reference point for specific playlist tracks, @@ -349,8 +336,6 @@ class Window(QMainWindow, Ui_MainWindow): self.previous_track_position: Optional[float] = None self.selected_plrs: Optional[List[PlaylistRows]] = None - self.signals = MusicMusterSignals() - self.set_main_window_size() self.lblSumPlaytime = QLabel("") self.statusbar.addPermanentWidget(self.lblSumPlaytime) @@ -379,6 +364,7 @@ class Window(QMainWindow, Ui_MainWindow): self.timer10.start(10) self.timer500.start(500) self.timer1000.start(1000) + self.signals = MusicMusterSignals() self.connect_signals_slots() def about(self) -> None: @@ -731,7 +717,6 @@ class Window(QMainWindow, Ui_MainWindow): playlist_tab = PlaylistTab( musicmuster=self, playlist_id=playlist.id, - signals=self.signals, ) idx = self.tabPlaylist.addTab(playlist_tab, playlist.name) self.tabPlaylist.setCurrentIndex(idx) @@ -1065,7 +1050,7 @@ class Window(QMainWindow, Ui_MainWindow): with Session() as session: dlg = TrackSelectDialog( session=session, - signals=self.signals, + new_row_number=self.active_tab().get_selected_row_number(), playlist_id=self.active_tab().playlist_id, ) dlg.exec() @@ -1923,7 +1908,7 @@ class TrackSelectDialog(QDialog): def __init__( self, session: scoped_session, - signals: MusicMusterSignals, + new_row_number: int, playlist_id: int, *args, **kwargs, @@ -1934,7 +1919,7 @@ class TrackSelectDialog(QDialog): super().__init__(*args, **kwargs) self.session = session - self.signals = signals + self.new_row_number = new_row_number self.playlist_id = playlist_id self.ui = Ui_Dialog() self.ui.setupUi(self) @@ -1946,6 +1931,7 @@ class TrackSelectDialog(QDialog): self.ui.radioTitle.toggled.connect(self.title_artist_toggle) self.ui.searchString.textEdited.connect(self.chars_typed) self.track: Optional[Tracks] = None + self.signals = MusicMusterSignals() record = Settings.get_int_settings(self.session, "dbdialog_width") width = record.f_int or 800 @@ -1974,7 +1960,9 @@ class TrackSelectDialog(QDialog): track_id = None if track: track_id = track.id - self.signals.add_track_to_playlist_signal.emit(self.playlist_id, track_id, note) + self.signals.add_track_to_playlist_signal.emit( + self.playlist_id, self.new_row_number, track_id, note + ) def add_selected_and_close(self) -> None: """Handle Add and Close button""" diff --git a/app/playlistmodel.py b/app/playlistmodel.py index 30ae274..55e2685 100644 --- a/app/playlistmodel.py +++ b/app/playlistmodel.py @@ -19,14 +19,15 @@ from PyQt6.QtGui import ( ) from config import Config -from playlists import PlaylistTab +from datastructures import MusicMusterSignals +from dbconfig import scoped_session, Session from helpers import ( file_is_unreadable, ) from models import PlaylistRows, Tracks -if TYPE_CHECKING: - from musicmuster import MusicMusterSignals + +HEADER_NOTES_COLUMN = 1 class Col(Enum): @@ -98,18 +99,15 @@ class PlaylistModel(QAbstractTableModel): def __init__( self, - playlist: PlaylistTab, playlist_id: int, - signals: "MusicMusterSignals", *args, **kwargs, ): - self.playlist = playlist self.playlist_id = playlist_id - self.signals = signals super().__init__(*args, **kwargs) self.playlist_rows: dict[int, PlaylistRowData] = {} + self.signals = MusicMusterSignals() self.signals.add_track_to_playlist_signal.connect(self.add_track) diff --git a/app/playlists.py b/app/playlists.py index 0b8c97a..6f5fb95 100644 --- a/app/playlists.py +++ b/app/playlists.py @@ -36,8 +36,9 @@ from PyQt6.QtWidgets import ( QStyleOption, ) -from config import Config +from datastructures import MusicMusterSignals from dbconfig import Session, scoped_session +from config import Config from helpers import ( ask_yes_no, file_is_unreadable, @@ -48,10 +49,8 @@ from helpers import ( set_track_metadata, ) from log import log - from models import Playlists, PlaylistRows, Settings, Tracks, NoteColours - -import playlistmodel +from playlistmodel import PlaylistModel if TYPE_CHECKING: from musicmuster import Window, MusicMusterSignals @@ -66,9 +65,9 @@ class EscapeDelegate(QStyledItemDelegate): - checks with user before abandoning edit on Escape """ - def __init__(self, parent, signals: "MusicMusterSignals") -> None: + def __init__(self, parent) -> None: super().__init__(parent) - self.signals = signals + self.signals = MusicMusterSignals() def createEditor( self, @@ -80,7 +79,7 @@ class EscapeDelegate(QStyledItemDelegate): Intercept createEditor call and make row just a little bit taller """ - self.signals.enable_escape_signal.emit(False) + signals.enable_escape_signal.emit(False) if isinstance(self.parent(), PlaylistTab): p = cast(PlaylistTab, self.parent()) if isinstance(index.data(), str): @@ -156,17 +155,15 @@ class PlaylistTab(QTableView): self, musicmuster: "Window", playlist_id: int, - signals: "MusicMusterSignals", ) -> None: super().__init__() # Save passed settings self.musicmuster = musicmuster self.playlist_id = playlist_id - self.signals = signals # Set up widget - self.setItemDelegate(EscapeDelegate(self, self.signals)) + self.setItemDelegate(EscapeDelegate(self)) self.setAlternatingRowColors(True) self.setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection) self.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows) @@ -194,6 +191,7 @@ class PlaylistTab(QTableView): h_header.setStretchLastSection(True) # self.itemSelectionChanged.connect(self._select_event) # self.signals.set_next_track_signal.connect(self._reset_next) + self.signals = MusicMusterSignals() self.signals.span_cells_signal.connect(self._span_cells) # Call self.eventFilter() for events @@ -205,7 +203,7 @@ class PlaylistTab(QTableView): # self.edit_cell_type: Optional[int] # Load playlist rows - self.setModel(playlistmodel.PlaylistModel(self, playlist_id, signals)) + self.setModel(PlaylistModel(playlist_id)) self._set_column_widths() # kae def __repr__(self) -> str: diff --git a/test_playlistmodel.py b/test_playlistmodel.py index 7981e8d..38eb95b 100644 --- a/test_playlistmodel.py +++ b/test_playlistmodel.py @@ -4,13 +4,12 @@ from app.models import ( from app import playlistmodel from dbconfig import scoped_session - def create_model_with_playlist_rows( session: scoped_session, rows: int ) -> "playlistmodel.PlaylistModel": playlist = Playlists(session, "test playlist") # Create a model - model = playlistmodel.PlaylistModel(playlist.id, None) + model = playlistmodel.PlaylistModel(playlist.id) for row in range(rows): plr = model._insert_row(session, row) newrow = plr.plr_rownum