WIP: template management: new, rename, delete working
This commit is contained in:
parent
e10c2adafe
commit
5f396a0993
@ -203,12 +203,12 @@ class Playlists(dbtables.PlaylistsTable):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_playlist_from_template(
|
def create_playlist_from_template(
|
||||||
cls, session: Session, template: "Playlists", playlist_name: str
|
cls, session: Session, template_id: int, playlist_name: str
|
||||||
) -> Optional["Playlists"]:
|
) -> Optional["Playlists"]:
|
||||||
"""Create a new playlist from template"""
|
"""Create a new playlist from template"""
|
||||||
|
|
||||||
# Sanity check
|
# Sanity check
|
||||||
if not template.id:
|
if not template_id:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
playlist = cls(session, playlist_name)
|
playlist = cls(session, playlist_name)
|
||||||
@ -217,7 +217,7 @@ class Playlists(dbtables.PlaylistsTable):
|
|||||||
if not playlist or not playlist.id:
|
if not playlist or not playlist.id:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
PlaylistRows.copy_playlist(session, template.id, playlist.id)
|
PlaylistRows.copy_playlist(session, template_id, playlist.id)
|
||||||
|
|
||||||
return playlist
|
return playlist
|
||||||
|
|
||||||
|
|||||||
@ -83,18 +83,6 @@ from utilities import check_db, update_bitrates
|
|||||||
import helpers
|
import helpers
|
||||||
|
|
||||||
|
|
||||||
class DownloadCSV(QDialog):
|
|
||||||
def __init__(self, parent=None):
|
|
||||||
super().__init__()
|
|
||||||
|
|
||||||
self.ui = Ui_DateSelect()
|
|
||||||
self.ui.setupUi(self)
|
|
||||||
self.ui.dateTimeEdit.setDate(QDate.currentDate())
|
|
||||||
self.ui.dateTimeEdit.setTime(QTime(19, 59, 0))
|
|
||||||
self.ui.buttonBox.accepted.connect(self.accept)
|
|
||||||
self.ui.buttonBox.rejected.connect(self.reject)
|
|
||||||
|
|
||||||
|
|
||||||
class Current:
|
class Current:
|
||||||
base_model: PlaylistModel
|
base_model: PlaylistModel
|
||||||
proxy_model: PlaylistProxyModel
|
proxy_model: PlaylistProxyModel
|
||||||
@ -267,7 +255,16 @@ class ItemlistManager(QDialog):
|
|||||||
self.callbacks.edit(item_id)
|
self.callbacks.edit(item_id)
|
||||||
|
|
||||||
def rename_item(self, item_id: int) -> None:
|
def rename_item(self, item_id: int) -> None:
|
||||||
print(f"Rename item {item_id}")
|
new_name = self.callbacks.rename(item_id)
|
||||||
|
if not new_name:
|
||||||
|
return
|
||||||
|
# Rename item in list
|
||||||
|
for row in range(self.table.rowCount()):
|
||||||
|
item = self.table.item(row, 0)
|
||||||
|
if item and self.items[row].id == item_id:
|
||||||
|
item.setText(new_name)
|
||||||
|
self.items[row].name = new_name
|
||||||
|
break
|
||||||
|
|
||||||
def toggle_favourite(self, item_id: int, checked: bool) -> None:
|
def toggle_favourite(self, item_id: int, checked: bool) -> None:
|
||||||
print(f"Toggle favourite for item {item_id}: {checked}")
|
print(f"Toggle favourite for item {item_id}: {checked}")
|
||||||
@ -294,7 +291,7 @@ class ItemlistManagerCallbacks:
|
|||||||
edit: Callable[[int], None]
|
edit: Callable[[int], None]
|
||||||
favourite: Callable[[int, bool], None]
|
favourite: Callable[[int, bool], None]
|
||||||
new_item: Callable[[], None]
|
new_item: Callable[[], None]
|
||||||
rename: Callable[[int], None]
|
rename: Callable[[int], Optional[str]]
|
||||||
|
|
||||||
|
|
||||||
class PreviewManager:
|
class PreviewManager:
|
||||||
@ -459,16 +456,21 @@ class TemplateSelectorDialog(QDialog):
|
|||||||
Class to manage user selection of template
|
Class to manage user selection of template
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, templates: list[tuple[str, int]]) -> None:
|
def __init__(
|
||||||
|
self, templates: list[tuple[str, int]], template_prompt: Optional[str]
|
||||||
|
) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.templates = templates
|
self.templates = templates
|
||||||
|
self.template_prompt = template_prompt
|
||||||
self.selected_id = None
|
self.selected_id = None
|
||||||
|
|
||||||
self.init_ui()
|
self.init_ui()
|
||||||
|
|
||||||
def init_ui(self):
|
def init_ui(self):
|
||||||
# Create label
|
# Create label
|
||||||
label = QLabel("Select template:")
|
if not self.template_prompt:
|
||||||
|
self.template_prompt = "Select template:"
|
||||||
|
label = QLabel(self.template_prompt)
|
||||||
|
|
||||||
# Create combo box
|
# Create combo box
|
||||||
self.combo_box = QComboBox()
|
self.combo_box = QComboBox()
|
||||||
@ -868,14 +870,22 @@ class Window(QMainWindow):
|
|||||||
self.signals.search_songfacts_signal.connect(self.open_songfacts_browser)
|
self.signals.search_songfacts_signal.connect(self.open_songfacts_browser)
|
||||||
self.signals.search_wikipedia_signal.connect(self.open_wikipedia_browser)
|
self.signals.search_wikipedia_signal.connect(self.open_wikipedia_browser)
|
||||||
|
|
||||||
def create_playlist(
|
def create_playlist(self, session: Session, template_id: int) -> Optional[Playlists]:
|
||||||
self, session: Session, playlist_name: str
|
|
||||||
) -> Optional[Playlists]:
|
|
||||||
"""Create new playlist"""
|
"""Create new playlist"""
|
||||||
|
|
||||||
log.debug(f"create_playlist({playlist_name=}")
|
# Get a name for this new playlist
|
||||||
|
playlist_name = self.solicit_playlist_name(session)
|
||||||
|
if not playlist_name:
|
||||||
|
return None
|
||||||
|
|
||||||
playlist = Playlists(session, playlist_name)
|
# If template.id == 0, user doesn't want a template
|
||||||
|
playlist: Optional[Playlists]
|
||||||
|
if template_id == 0:
|
||||||
|
playlist = Playlists(session, playlist_name)
|
||||||
|
else:
|
||||||
|
playlist = Playlists.create_playlist_from_template(
|
||||||
|
session, template_id, playlist_name
|
||||||
|
)
|
||||||
|
|
||||||
if playlist:
|
if playlist:
|
||||||
return playlist
|
return playlist
|
||||||
@ -1072,6 +1082,18 @@ class Window(QMainWindow):
|
|||||||
if track_sequence.current:
|
if track_sequence.current:
|
||||||
track_sequence.current.fade()
|
track_sequence.current.fade()
|
||||||
|
|
||||||
|
def get_tab_index_for_playlist(self, playlist_id: int) -> Optional[int]:
|
||||||
|
"""
|
||||||
|
Return the tab index for the passed playlist_id if it is displayed,
|
||||||
|
else return None.
|
||||||
|
"""
|
||||||
|
|
||||||
|
for idx in range(self.playlist_section.tabPlaylist.count()):
|
||||||
|
if self.playlist_section.tabPlaylist.widget(idx).playlist_id == playlist_id:
|
||||||
|
return idx
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
def hide_played(self):
|
def hide_played(self):
|
||||||
"""Toggle hide played tracks"""
|
"""Toggle hide played tracks"""
|
||||||
|
|
||||||
@ -1178,6 +1200,7 @@ class Window(QMainWindow):
|
|||||||
Delete / edit templates
|
Delete / edit templates
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# Define callbacks to handle management options
|
||||||
def delete(template_id: int) -> None:
|
def delete(template_id: int) -> None:
|
||||||
"""delete template"""
|
"""delete template"""
|
||||||
|
|
||||||
@ -1191,19 +1214,15 @@ class Window(QMainWindow):
|
|||||||
f"Delete template '{template.name}': " "Are you sure?",
|
f"Delete template '{template.name}': " "Are you sure?",
|
||||||
):
|
):
|
||||||
# If template is currently open, re-check
|
# If template is currently open, re-check
|
||||||
for idx in range(self.playlist_section.tabPlaylist.count()):
|
open_idx = self.get_tab_index_for_playlist(template_id)
|
||||||
if (
|
if open_idx:
|
||||||
self.playlist_section.tabPlaylist.widget(idx).playlist_id
|
if not helpers.ask_yes_no(
|
||||||
== template_id
|
"Delete open template",
|
||||||
|
f"Template '{template.name}' is currently open. Really delete?",
|
||||||
):
|
):
|
||||||
if not helpers.ask_yes_no(
|
return
|
||||||
"Delete open template",
|
else:
|
||||||
f"Template '{template.name}' is currently open. Really delete?"
|
self.playlist_section.tabPlaylist.removeTab(open_idx)
|
||||||
):
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
self.playlist_section.tabPlaylist.removeTab(idx)
|
|
||||||
break
|
|
||||||
|
|
||||||
log.info(f"manage_templates: delete {template=}")
|
log.info(f"manage_templates: delete {template=}")
|
||||||
template.delete(session)
|
template.delete(session)
|
||||||
@ -1223,25 +1242,50 @@ class Window(QMainWindow):
|
|||||||
self.playlist_section.tabPlaylist.setCurrentIndex(idx)
|
self.playlist_section.tabPlaylist.setCurrentIndex(idx)
|
||||||
|
|
||||||
def favourite(template_id: int, favourite: bool) -> None:
|
def favourite(template_id: int, favourite: bool) -> None:
|
||||||
"""favourite template"""
|
"""Mark template as (not) favourite"""
|
||||||
|
|
||||||
print(f"manage_templates.favourite({template_id=}")
|
print(f"manage_templates.favourite({template_id=}")
|
||||||
print(f"{session=}")
|
print(f"{session=}")
|
||||||
|
|
||||||
def new_item() -> None:
|
def new_item() -> None:
|
||||||
"""new item"""
|
"""Create new template"""
|
||||||
|
|
||||||
print("manage_templates.new()")
|
# Get base template
|
||||||
print(f"{session=}")
|
template_id = self.solicit_template_to_use(
|
||||||
|
session, template_prmompt="New template based upon:"
|
||||||
|
)
|
||||||
|
if template_id is None:
|
||||||
|
return
|
||||||
|
|
||||||
def rename(template_id: int) -> None:
|
new_template = self.create_playlist(session, template_id)
|
||||||
|
if new_template:
|
||||||
|
self.open_playlist(session, new_template)
|
||||||
|
|
||||||
|
def rename(template_id: int) -> Optional[str]:
|
||||||
"""rename template"""
|
"""rename template"""
|
||||||
|
|
||||||
print(f"manage_templates.rename({template_id=}")
|
template = session.get(Playlists, template_id)
|
||||||
print(f"{session=}")
|
if not template:
|
||||||
|
raise ApplicationError(
|
||||||
|
f"manage_templeate.delete({template_id=}) can't load template"
|
||||||
|
)
|
||||||
|
new_name = self.solicit_playlist_name(session, template.name)
|
||||||
|
if new_name:
|
||||||
|
template.rename(session, new_name)
|
||||||
|
idx = self.tabBar.currentIndex()
|
||||||
|
self.tabBar.setTabText(idx, new_name)
|
||||||
|
session.commit()
|
||||||
|
return new_name
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Call listitem management dialog to manage templates
|
||||||
callbacks = ItemlistManagerCallbacks(
|
callbacks = ItemlistManagerCallbacks(
|
||||||
delete=delete, edit=edit, favourite=favourite, new_item=new_item, rename=rename
|
delete=delete,
|
||||||
|
edit=edit,
|
||||||
|
favourite=favourite,
|
||||||
|
new_item=new_item,
|
||||||
|
rename=rename,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Build a list of templates
|
# Build a list of templates
|
||||||
@ -1251,8 +1295,10 @@ class Window(QMainWindow):
|
|||||||
for template in Playlists.get_all_templates(session):
|
for template in Playlists.get_all_templates(session):
|
||||||
# TODO: need to add in favourites
|
# TODO: need to add in favourites
|
||||||
template_list.append(ItemlistItem(name=template.name, id=template.id))
|
template_list.append(ItemlistItem(name=template.name, id=template.id))
|
||||||
x = ItemlistManager(template_list, callbacks)
|
# We need to retain a reference to the dialog box to stop it
|
||||||
x.show()
|
# going out of scope and being garbage-collected.
|
||||||
|
self.dlg = ItemlistManager(template_list, callbacks)
|
||||||
|
self.dlg.show()
|
||||||
|
|
||||||
def mark_rows_for_moving(self) -> None:
|
def mark_rows_for_moving(self) -> None:
|
||||||
"""
|
"""
|
||||||
@ -1337,37 +1383,17 @@ class Window(QMainWindow):
|
|||||||
self.move_playlist_rows(unplayed_rows)
|
self.move_playlist_rows(unplayed_rows)
|
||||||
self.disable_selection_timing = False
|
self.disable_selection_timing = False
|
||||||
|
|
||||||
def new_playlist(self) -> None:
|
def new_playlist(self) -> Optional[Playlists]:
|
||||||
"""
|
"""
|
||||||
Create new playlist, optionally from template
|
Create new playlist, optionally from template
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Build a list of (template-name, playlist-id) tuples starting
|
|
||||||
# with the "no template" entry
|
|
||||||
template_list: list[tuple[str, int]] = []
|
|
||||||
template_list.append((Config.NO_TEMPLATE_NAME, 0))
|
|
||||||
|
|
||||||
with db.Session() as session:
|
with db.Session() as session:
|
||||||
for template in Playlists.get_all_templates(session):
|
template_id = self.solicit_template_to_use(session)
|
||||||
template_list.append((template.name, template.id))
|
if not template_id:
|
||||||
|
return None # User cancelled
|
||||||
|
|
||||||
dlg = TemplateSelectorDialog(template_list)
|
playlist = self.create_playlist(session, template_id)
|
||||||
if not dlg.exec():
|
|
||||||
return # User cancelled
|
|
||||||
template_id = dlg.selected_id
|
|
||||||
|
|
||||||
# Get a name for this new playlist
|
|
||||||
playlist_name = self.solicit_playlist_name(session)
|
|
||||||
if not playlist_name:
|
|
||||||
return
|
|
||||||
|
|
||||||
# If template_id == 0, user doesn't want a template
|
|
||||||
if template_id == 0:
|
|
||||||
playlist = self.create_playlist(session, playlist_name)
|
|
||||||
else:
|
|
||||||
playlist = Playlists.create_playlist_from_template(
|
|
||||||
session, template, playlist_name
|
|
||||||
)
|
|
||||||
|
|
||||||
if playlist:
|
if playlist:
|
||||||
playlist.mark_open()
|
playlist.mark_open()
|
||||||
@ -1376,10 +1402,13 @@ class Window(QMainWindow):
|
|||||||
session.commit()
|
session.commit()
|
||||||
idx = self.create_playlist_tab(playlist)
|
idx = self.create_playlist_tab(playlist)
|
||||||
self.playlist_section.tabPlaylist.setCurrentIndex(idx)
|
self.playlist_section.tabPlaylist.setCurrentIndex(idx)
|
||||||
|
return playlist
|
||||||
else:
|
else:
|
||||||
log.error("Playlist failed to create")
|
ApplicationError("new_playlist: Playlist failed to create")
|
||||||
|
|
||||||
def open_playlist(self) -> None:
|
return None
|
||||||
|
|
||||||
|
def open_existing_playlist(self) -> None:
|
||||||
"""Open existing playlist"""
|
"""Open existing playlist"""
|
||||||
|
|
||||||
with db.Session() as session:
|
with db.Session() as session:
|
||||||
@ -1388,11 +1417,16 @@ class Window(QMainWindow):
|
|||||||
dlg.exec()
|
dlg.exec()
|
||||||
playlist = dlg.playlist
|
playlist = dlg.playlist
|
||||||
if playlist:
|
if playlist:
|
||||||
idx = self.create_playlist_tab(playlist)
|
self.open_playlist(session, playlist)
|
||||||
playlist.mark_open()
|
|
||||||
session.commit()
|
|
||||||
|
|
||||||
self.playlist_section.tabPlaylist.setCurrentIndex(idx)
|
def open_playlist(self, session: Session, playlist: Playlists) -> None:
|
||||||
|
"""Open passed playlist"""
|
||||||
|
|
||||||
|
idx = self.create_playlist_tab(playlist)
|
||||||
|
playlist.mark_open()
|
||||||
|
session.commit()
|
||||||
|
|
||||||
|
self.playlist_section.tabPlaylist.setCurrentIndex(idx)
|
||||||
|
|
||||||
def open_songfacts_browser(self, title: str) -> None:
|
def open_songfacts_browser(self, title: str) -> None:
|
||||||
"""Search Songfacts for title"""
|
"""Search Songfacts for title"""
|
||||||
@ -1859,24 +1893,45 @@ class Window(QMainWindow):
|
|||||||
|
|
||||||
# Switch to correct tab
|
# Switch to correct tab
|
||||||
if playlist_id != self.current.playlist_id:
|
if playlist_id != self.current.playlist_id:
|
||||||
for idx in range(self.playlist_section.tabPlaylist.count()):
|
open_idx = self.get_tab_index_for_playlist(playlist_id)
|
||||||
if (
|
if open_idx:
|
||||||
self.playlist_section.tabPlaylist.widget(idx).playlist_id
|
self.playlist_section.tabPlaylist.setCurrentIndex(open_idx)
|
||||||
== playlist_id
|
else:
|
||||||
):
|
raise ApplicationError(
|
||||||
self.playlist_section.tabPlaylist.setCurrentIndex(idx)
|
f"show_track() can't find current playlist tab {playlist_id=}"
|
||||||
break
|
)
|
||||||
|
|
||||||
self.active_tab().scroll_to_top(playlist_track.row_number)
|
self.active_tab().scroll_to_top(playlist_track.row_number)
|
||||||
|
|
||||||
|
def solicit_template_to_use(
|
||||||
|
self, session: Session, template_prmompt: Optional[str] = None
|
||||||
|
) -> Optional[int]:
|
||||||
|
"""
|
||||||
|
Have user select a template. Return the template.id, or None if they cancel.
|
||||||
|
template_id of zero means don't use a template.
|
||||||
|
"""
|
||||||
|
|
||||||
|
template_name_id_list: list[tuple[str, int]] = []
|
||||||
|
template_name_id_list.append((Config.NO_TEMPLATE_NAME, 0))
|
||||||
|
|
||||||
|
with db.Session() as session:
|
||||||
|
for template in Playlists.get_all_templates(session):
|
||||||
|
template_name_id_list.append((template.name, template.id))
|
||||||
|
|
||||||
|
dlg = TemplateSelectorDialog(template_name_id_list, template_prmompt)
|
||||||
|
if not dlg.exec() or dlg.selected_id is None:
|
||||||
|
return None # User cancelled
|
||||||
|
|
||||||
|
return dlg.selected_id
|
||||||
|
|
||||||
def solicit_playlist_name(
|
def solicit_playlist_name(
|
||||||
self, session: Session, default: str = ""
|
self, session: Session, default: str = "", prompt: str = "Playlist name:"
|
||||||
) -> Optional[str]:
|
) -> Optional[str]:
|
||||||
"""Get name of new playlist from user"""
|
"""Get name of new playlist from user"""
|
||||||
|
|
||||||
dlg = QInputDialog(self)
|
dlg = QInputDialog(self)
|
||||||
dlg.setInputMode(QInputDialog.InputMode.TextInput)
|
dlg.setInputMode(QInputDialog.InputMode.TextInput)
|
||||||
dlg.setLabelText("Playlist name:")
|
dlg.setLabelText(prompt)
|
||||||
while True:
|
while True:
|
||||||
if default:
|
if default:
|
||||||
dlg.setTextValue(default)
|
dlg.setTextValue(default)
|
||||||
|
|||||||
@ -26,6 +26,7 @@ from PyQt6.QtGui import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Third party imports
|
# Third party imports
|
||||||
|
from sqlalchemy.orm.session import Session
|
||||||
import obswebsocket # type: ignore
|
import obswebsocket # type: ignore
|
||||||
|
|
||||||
# import snoop # type: ignore
|
# import snoop # type: ignore
|
||||||
@ -772,7 +773,7 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def load_data(self, session: db.session) -> None:
|
def load_data(self, session: Session) -> None:
|
||||||
"""
|
"""
|
||||||
Same as refresh data, but only used when creating playslit.
|
Same as refresh data, but only used when creating playslit.
|
||||||
Distinguishes profile time between initial load and other
|
Distinguishes profile time between initial load and other
|
||||||
@ -1061,7 +1062,7 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
# Update display
|
# Update display
|
||||||
self.invalidate_row(track_sequence.previous.row_number)
|
self.invalidate_row(track_sequence.previous.row_number)
|
||||||
|
|
||||||
def refresh_data(self, session: db.session) -> None:
|
def refresh_data(self, session: Session) -> None:
|
||||||
"""
|
"""
|
||||||
Populate self.playlist_rows with playlist data
|
Populate self.playlist_rows with playlist data
|
||||||
|
|
||||||
|
|||||||
@ -13,7 +13,7 @@ menus:
|
|||||||
- title: "&Playlist"
|
- title: "&Playlist"
|
||||||
actions:
|
actions:
|
||||||
- text: "Open Playlist"
|
- text: "Open Playlist"
|
||||||
handler: "open_playlist"
|
handler: "open_existing_playlist"
|
||||||
shortcut: "Ctrl+O"
|
shortcut: "Ctrl+O"
|
||||||
- text: "New Playlist"
|
- text: "New Playlist"
|
||||||
handler: "new_playlist_dynamic_submenu"
|
handler: "new_playlist_dynamic_submenu"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user