WIP V3: insert track works
This commit is contained in:
parent
e4b986fd2e
commit
3557d22c54
@ -31,11 +31,10 @@ def Session() -> Generator[scoped_session, None, None]:
|
||||
function = frame.function
|
||||
lineno = frame.lineno
|
||||
Session = scoped_session(sessionmaker(bind=engine))
|
||||
log.debug(f"SqlA: session acquired [{hex(id(Session))}]")
|
||||
log.debug(
|
||||
f"Session acquisition: {file}:{function}:{lineno} " f"[{hex(id(Session))}]"
|
||||
f"Session acquired: {file}:{function}:{lineno} " f"[{hex(id(Session))}]"
|
||||
)
|
||||
yield Session
|
||||
log.debug(f" SqlA: session released [{hex(id(Session))}]")
|
||||
log.debug(f" Session released [{hex(id(Session))}]")
|
||||
Session.commit()
|
||||
Session.close()
|
||||
|
||||
@ -71,7 +71,7 @@ from models import Base, Carts, Playdates, PlaylistRows, Playlists, Settings, Tr
|
||||
from config import Config
|
||||
from playlists import PlaylistTab
|
||||
from ui.dlg_cart_ui import Ui_DialogCartEdit # type: ignore
|
||||
from ui.dlg_search_database_ui import Ui_Dialog # type: ignore
|
||||
from ui.dlg_TrackSelect_ui import Ui_Dialog # type: ignore
|
||||
from ui.dlg_SelectPlaylist_ui import Ui_dlgSelectPlaylist # type: ignore
|
||||
from ui.downloadcsv_ui import Ui_DateSelect # type: ignore
|
||||
from ui.main_window_ui import Ui_MainWindow # type: ignore
|
||||
@ -246,9 +246,10 @@ class MusicMusterSignals(QObject):
|
||||
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)
|
||||
enable_escape_signal = pyqtSignal(bool)
|
||||
add_track_to_playlist_signal = pyqtSignal(int, int, str)
|
||||
|
||||
|
||||
class PlaylistTrack:
|
||||
@ -745,9 +746,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
with Session() as session:
|
||||
# Save the selected PlaylistRows items ready for a later
|
||||
# paste
|
||||
self.selected_plrs = self.active_tab().get_selected_playlistrows(
|
||||
session
|
||||
)
|
||||
self.selected_plrs = self.active_tab().get_selected_playlistrows(session)
|
||||
|
||||
def debug(self):
|
||||
"""Invoke debugger"""
|
||||
@ -928,7 +927,10 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
def get_one_track(self, session: scoped_session) -> Optional[Tracks]:
|
||||
"""Show dialog box to select one track and return it to caller"""
|
||||
|
||||
dlg = DbDialog(self, session, get_one_track=True)
|
||||
dlg = TrackSelectDialog(self, session)
|
||||
dlg.ui.txtNote.hide()
|
||||
dlg.ui.lblNote.hide()
|
||||
|
||||
if dlg.exec():
|
||||
return dlg.track
|
||||
else:
|
||||
@ -1054,15 +1056,18 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
ok = dlg.exec()
|
||||
if ok:
|
||||
model.insert_header_row(
|
||||
self.active_tab().get_selected_row_number(),
|
||||
dlg.textValue()
|
||||
self.active_tab().get_selected_row_number(), dlg.textValue()
|
||||
)
|
||||
|
||||
def insert_track(self) -> None:
|
||||
"""Show dialog box to select and add track from database"""
|
||||
|
||||
with Session() as session:
|
||||
dlg = DbDialog(self, session, get_one_track=False)
|
||||
dlg = TrackSelectDialog(
|
||||
session=session,
|
||||
signals=self.signals,
|
||||
playlist_id=self.active_tab().playlist_id,
|
||||
)
|
||||
dlg.exec()
|
||||
|
||||
def load_last_playlists(self) -> None:
|
||||
@ -1159,9 +1164,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
"""
|
||||
|
||||
with Session() as session:
|
||||
selected_plrs = self.active_tab().get_selected_playlistrows(
|
||||
session
|
||||
)
|
||||
selected_plrs = self.active_tab().get_selected_playlistrows(session)
|
||||
if not selected_plrs:
|
||||
return
|
||||
|
||||
@ -1914,66 +1917,42 @@ class CartDialog(QDialog):
|
||||
self.ui.lblPath.setText(self.path)
|
||||
|
||||
|
||||
class DbDialog(QDialog):
|
||||
class TrackSelectDialog(QDialog):
|
||||
"""Select track from database"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
musicmuster: Window,
|
||||
session: scoped_session,
|
||||
get_one_track: bool = False,
|
||||
signals: MusicMusterSignals,
|
||||
playlist_id: int,
|
||||
*args,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
"""
|
||||
Subclassed QDialog to manage track selection
|
||||
|
||||
If get_one_track is True, return after first track selection
|
||||
with that track in ui.track. Otherwise, allow multiple tracks
|
||||
to be added to the playlist.
|
||||
"""
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
self.musicmuster = musicmuster
|
||||
self.session = session
|
||||
self.get_one_track = get_one_track
|
||||
self.signals = signals
|
||||
self.playlist_id = playlist_id
|
||||
self.ui = Ui_Dialog()
|
||||
self.ui.setupUi(self)
|
||||
self.ui.btnAdd.clicked.connect(self.add_selected)
|
||||
self.ui.btnAddClose.clicked.connect(self.add_selected_and_close)
|
||||
self.ui.btnClose.clicked.connect(self.close)
|
||||
self.ui.matchList.itemDoubleClicked.connect(self.double_click)
|
||||
self.ui.matchList.itemDoubleClicked.connect(self.add_selected)
|
||||
self.ui.matchList.itemSelectionChanged.connect(self.selection_changed)
|
||||
self.ui.radioTitle.toggled.connect(self.title_artist_toggle)
|
||||
self.ui.searchString.textEdited.connect(self.chars_typed)
|
||||
self.track: Optional[Tracks] = None
|
||||
|
||||
if get_one_track:
|
||||
self.ui.txtNote.hide()
|
||||
self.ui.lblNote.hide()
|
||||
|
||||
record = Settings.get_int_settings(self.session, "dbdialog_width")
|
||||
width = record.f_int or 800
|
||||
record = Settings.get_int_settings(self.session, "dbdialog_height")
|
||||
height = record.f_int or 600
|
||||
self.resize(width, height)
|
||||
|
||||
def __del__(self) -> None:
|
||||
"""Save dialog size and position"""
|
||||
|
||||
# FIXME:
|
||||
# if record.f_int != self.height():
|
||||
# ^^^^^^^^^^^^^
|
||||
# RuntimeError: wrapped C/C++ object of type DbDialog has been deleted
|
||||
|
||||
record = Settings.get_int_settings(self.session, "dbdialog_height")
|
||||
if record.f_int != self.height():
|
||||
record.update(self.session, {"f_int": self.height()})
|
||||
|
||||
record = Settings.get_int_settings(self.session, "dbdialog_width")
|
||||
if record.f_int != self.width():
|
||||
record.update(self.session, {"f_int": self.width()})
|
||||
|
||||
def add_selected(self) -> None:
|
||||
"""Handle Add button"""
|
||||
|
||||
@ -1989,7 +1968,13 @@ class DbDialog(QDialog):
|
||||
if not note and not track:
|
||||
return
|
||||
|
||||
self.add_track(track, self.ui.txtNote.text())
|
||||
self.ui.txtNote.clear()
|
||||
self.select_searchtext()
|
||||
|
||||
track_id = None
|
||||
if track:
|
||||
track_id = track.id
|
||||
self.signals.add_track_to_playlist_signal.emit(self.playlist_id, track_id, note)
|
||||
|
||||
def add_selected_and_close(self) -> None:
|
||||
"""Handle Add and Close button"""
|
||||
@ -1997,24 +1982,6 @@ class DbDialog(QDialog):
|
||||
self.add_selected()
|
||||
self.accept()
|
||||
|
||||
def add_track(self, track: Optional[Tracks], note: str) -> None:
|
||||
"""Add passed track to playlist on screen"""
|
||||
|
||||
if self.get_one_track:
|
||||
self.track = track
|
||||
self.accept()
|
||||
return
|
||||
|
||||
if track:
|
||||
self.musicmuster.active_tab().insert_track(
|
||||
self.session, track, note
|
||||
)
|
||||
else:
|
||||
self.musicmuster.active_tab().insert_header(self.session, note)
|
||||
|
||||
self.ui.txtNote.clear()
|
||||
self.select_searchtext()
|
||||
|
||||
def chars_typed(self, s: str) -> None:
|
||||
"""Handle text typed in search box"""
|
||||
|
||||
@ -2042,12 +2009,23 @@ class DbDialog(QDialog):
|
||||
t.setData(Qt.ItemDataRole.UserRole, track)
|
||||
self.ui.matchList.addItem(t)
|
||||
|
||||
def double_click(self, entry: QListWidgetItem) -> None:
|
||||
"""Add items that are double-clicked"""
|
||||
def closeEvent(self, event: Optional[QEvent]) -> None:
|
||||
"""
|
||||
Override close and save dialog coordinates
|
||||
"""
|
||||
|
||||
track = entry.data(Qt.ItemDataRole.UserRole)
|
||||
note = self.ui.txtNote.text()
|
||||
self.add_track(track, note)
|
||||
if not event:
|
||||
return
|
||||
|
||||
record = Settings.get_int_settings(self.session, "dbdialog_height")
|
||||
if record.f_int != self.height():
|
||||
record.update(self.session, {"f_int": self.height()})
|
||||
|
||||
record = Settings.get_int_settings(self.session, "dbdialog_width")
|
||||
if record.f_int != self.width():
|
||||
record.update(self.session, {"f_int": self.width()})
|
||||
|
||||
event.accept()
|
||||
|
||||
def keyPressEvent(self, event):
|
||||
"""
|
||||
@ -2059,7 +2037,7 @@ class DbDialog(QDialog):
|
||||
self.ui.matchList.clearSelection()
|
||||
return
|
||||
|
||||
super(DbDialog, self).keyPressEvent(event)
|
||||
super(TrackSelectDialog, self).keyPressEvent(event)
|
||||
|
||||
def select_searchtext(self) -> None:
|
||||
"""Select the searchbox"""
|
||||
|
||||
@ -19,11 +19,10 @@ from PyQt6.QtGui import (
|
||||
)
|
||||
|
||||
from config import Config
|
||||
|
||||
from playlists import PlaylistTab
|
||||
from helpers import (
|
||||
file_is_unreadable,
|
||||
)
|
||||
|
||||
from models import PlaylistRows, Tracks
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@ -98,14 +97,22 @@ class PlaylistModel(QAbstractTableModel):
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, playlist_id: int, signals: "MusicMusterSignals", *args, **kwargs
|
||||
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.add_track_to_playlist_signal.connect(self.add_track)
|
||||
|
||||
with Session() as session:
|
||||
self.refresh_data(session)
|
||||
|
||||
@ -114,6 +121,29 @@ class PlaylistModel(QAbstractTableModel):
|
||||
f"<PlaylistModel: playlist_id={self.playlist_id}, {self.rowCount()} rows>"
|
||||
)
|
||||
|
||||
def add_track(
|
||||
self, playlist_id: int, track: Optional[Tracks], note: Optional[str]
|
||||
) -> None:
|
||||
"""
|
||||
Add track if it's for our playlist
|
||||
"""
|
||||
|
||||
# Ignore if it's not for us
|
||||
if playlist_id != self.playlist_id:
|
||||
return
|
||||
|
||||
row_number = self.playlist.get_selected_row_number()
|
||||
|
||||
# Insert track if we have one
|
||||
if track:
|
||||
self.insert_track_row(row_number, track, note)
|
||||
# If we only have a note, add as a header row
|
||||
elif note:
|
||||
self.insert_header_row(row_number, note)
|
||||
else:
|
||||
# No track, no note, no point
|
||||
return
|
||||
|
||||
def background_role(self, row: int, column: int, prd: PlaylistRowData) -> QBrush:
|
||||
"""Return background setting"""
|
||||
|
||||
@ -299,7 +329,7 @@ class PlaylistModel(QAbstractTableModel):
|
||||
|
||||
def insert_header_row(self, row_number: Optional[int], text: str) -> None:
|
||||
"""
|
||||
Insert a header row. Return row number or None if insertion failed.
|
||||
Insert a header row.
|
||||
"""
|
||||
|
||||
with Session() as session:
|
||||
@ -347,6 +377,24 @@ class PlaylistModel(QAbstractTableModel):
|
||||
# Insert the new row and return it
|
||||
return PlaylistRows(session, self.playlist_id, new_row_number)
|
||||
|
||||
def insert_track_row(
|
||||
self, row_number: Optional[int], track_id: int, text: Optional[str]
|
||||
) -> None:
|
||||
"""
|
||||
Insert a track row.
|
||||
"""
|
||||
|
||||
with Session() as session:
|
||||
plr = self._insert_row(session, row_number)
|
||||
# Update the PlaylistRows object
|
||||
plr.track_id = track_id
|
||||
if text:
|
||||
plr.note = text
|
||||
# Repopulate self.playlist_rows
|
||||
self.refresh_data(session)
|
||||
# Update the display from the new row onwards
|
||||
self.invalidate_rows(list(range(plr.plr_rownum, len(self.playlist_rows))))
|
||||
|
||||
def invalidate_row(self, modified_row: int) -> None:
|
||||
"""
|
||||
Signal to view to refresh invlidated row
|
||||
@ -394,7 +442,8 @@ class PlaylistModel(QAbstractTableModel):
|
||||
[y for y in range(len(self.playlist_rows)) if y not in row_map.values()],
|
||||
):
|
||||
# Optimise: only add to map if there is a change
|
||||
row_map[old_row] = new_row
|
||||
if old_row != new_row:
|
||||
row_map[old_row] = new_row
|
||||
|
||||
# For SQLAlchemy, build a list of dictionaries that map plrid to
|
||||
# new row number:
|
||||
@ -404,7 +453,7 @@ class PlaylistModel(QAbstractTableModel):
|
||||
sqla_map.append({"plrid": plrid, "plr_rownum": newrow})
|
||||
|
||||
# Update database. Ref:
|
||||
# https://docs.sqlalchemy.org/en/20/core/sqlelement.html#sqlalchemy.sql.expression.case
|
||||
# https://docs.sqlalchemy.org/en/20/tutorial/data_update.html#the-update-sql-expression-construct
|
||||
stmt = (
|
||||
update(PlaylistRows)
|
||||
.where(
|
||||
|
||||
@ -51,7 +51,7 @@ from log import log
|
||||
|
||||
from models import Playlists, PlaylistRows, Settings, Tracks, NoteColours
|
||||
|
||||
from playlistmodel import PlaylistModel
|
||||
import playlistmodel
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from musicmuster import Window, MusicMusterSignals
|
||||
@ -205,7 +205,7 @@ class PlaylistTab(QTableView):
|
||||
# self.edit_cell_type: Optional[int]
|
||||
|
||||
# Load playlist rows
|
||||
self.setModel(PlaylistModel(playlist_id, signals))
|
||||
self.setModel(playlistmodel.PlaylistModel(self, playlist_id, signals))
|
||||
self._set_column_widths()
|
||||
|
||||
# kae def __repr__(self) -> str:
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# Form implementation generated from reading ui file 'app/ui/dlg_SearchDatabase.ui'
|
||||
# Form implementation generated from reading ui file 'dlg_TrackSelect.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.5.2
|
||||
# Created by: PyQt6 UI code generator 6.5.3
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
Loading…
Reference in New Issue
Block a user