parent
937f3cd074
commit
b14b90396f
@ -1,7 +1,7 @@
|
||||
# Standard library imports
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from dataclasses import dataclass, field
|
||||
from enum import auto, Enum
|
||||
import functools
|
||||
from typing import NamedTuple
|
||||
@ -13,7 +13,6 @@ from PyQt6.QtCore import (
|
||||
pyqtSignal,
|
||||
QObject,
|
||||
)
|
||||
from PyQt6.QtGui import QAction
|
||||
|
||||
# App imports
|
||||
|
||||
@ -94,6 +93,13 @@ class MusicMusterSignals(QObject):
|
||||
super().__init__()
|
||||
|
||||
|
||||
@singleton
|
||||
@dataclass
|
||||
class Selection:
|
||||
playlist_id: int = 0
|
||||
rows: list[int] = field(default_factory=list)
|
||||
|
||||
|
||||
class Tags(NamedTuple):
|
||||
artist: str
|
||||
title: str
|
||||
|
||||
@ -106,6 +106,7 @@ class Config(object):
|
||||
SECTION_STARTS = ("+", "+-", "-+")
|
||||
SONGFACTS_ON_NEXT = False
|
||||
START_GAP_WARNING_THRESHOLD = 300
|
||||
SUBTOTAL_ON_ROW_ZERO = "[No subtotal on first row]"
|
||||
TEXT_NO_TRACK_NO_NOTE = "[Section header]"
|
||||
TOD_TIME_FORMAT = "%H:%M:%S"
|
||||
TRACK_TIME_FORMAT = "%H:%M:%S"
|
||||
|
||||
@ -40,7 +40,7 @@ class TrackSelectDialog(QDialog):
|
||||
parent: QMainWindow,
|
||||
session: Session,
|
||||
new_row_number: int,
|
||||
source_model: PlaylistModel,
|
||||
base_model: PlaylistModel,
|
||||
add_to_header: Optional[bool] = False,
|
||||
*args: Qt.WindowType,
|
||||
**kwargs: Qt.WindowType,
|
||||
@ -52,7 +52,7 @@ class TrackSelectDialog(QDialog):
|
||||
super().__init__(parent, *args, **kwargs)
|
||||
self.session = session
|
||||
self.new_row_number = new_row_number
|
||||
self.source_model = source_model
|
||||
self.base_model = base_model
|
||||
self.add_to_header = add_to_header
|
||||
self.ui = dlg_TrackSelect_ui.Ui_Dialog()
|
||||
self.ui.setupUi(self)
|
||||
@ -96,7 +96,7 @@ class TrackSelectDialog(QDialog):
|
||||
track_id = track.id
|
||||
|
||||
if note and not track_id:
|
||||
self.source_model.insert_row(self.new_row_number, track_id, note)
|
||||
self.base_model.insert_row(self.new_row_number, track_id, note)
|
||||
self.ui.txtNote.clear()
|
||||
self.new_row_number += 1
|
||||
return
|
||||
@ -110,7 +110,7 @@ class TrackSelectDialog(QDialog):
|
||||
|
||||
# Check whether track is already in playlist
|
||||
move_existing = False
|
||||
existing_prd = self.source_model.is_track_in_playlist(track_id)
|
||||
existing_prd = self.base_model.is_track_in_playlist(track_id)
|
||||
if existing_prd is not None:
|
||||
if ask_yes_no(
|
||||
"Duplicate row",
|
||||
@ -121,21 +121,21 @@ class TrackSelectDialog(QDialog):
|
||||
|
||||
if self.add_to_header:
|
||||
if move_existing and existing_prd: # "and existing_prd" for mypy's benefit
|
||||
self.source_model.move_track_to_header(
|
||||
self.base_model.move_track_to_header(
|
||||
self.new_row_number, existing_prd, note
|
||||
)
|
||||
else:
|
||||
self.source_model.add_track_to_header(self.new_row_number, track_id)
|
||||
self.base_model.add_track_to_header(self.new_row_number, track_id)
|
||||
# Close dialog - we can only add one track to a header
|
||||
self.accept()
|
||||
else:
|
||||
# Adding a new track row
|
||||
if move_existing and existing_prd: # "and existing_prd" for mypy's benefit
|
||||
self.source_model.move_track_add_note(
|
||||
self.base_model.move_track_add_note(
|
||||
self.new_row_number, existing_prd, note
|
||||
)
|
||||
else:
|
||||
self.source_model.insert_row(self.new_row_number, track_id, note)
|
||||
self.base_model.insert_row(self.new_row_number, track_id, note)
|
||||
|
||||
self.new_row_number += 1
|
||||
|
||||
|
||||
@ -58,7 +58,7 @@ class DoTrackImport(QObject):
|
||||
destination_track_path: str,
|
||||
track_id: int,
|
||||
audio_metadata: AudioMetadata,
|
||||
source_model: PlaylistModel,
|
||||
base_model: PlaylistModel,
|
||||
row_number: Optional[int],
|
||||
) -> None:
|
||||
"""
|
||||
@ -72,10 +72,10 @@ class DoTrackImport(QObject):
|
||||
self.destination_track_path = destination_track_path
|
||||
self.track_id = track_id
|
||||
self.audio_metadata = audio_metadata
|
||||
self.source_model = source_model
|
||||
self.base_model = base_model
|
||||
|
||||
if row_number is None:
|
||||
self.next_row_number = source_model.rowCount()
|
||||
self.next_row_number = base_model.rowCount()
|
||||
else:
|
||||
self.next_row_number = row_number
|
||||
|
||||
@ -130,7 +130,7 @@ class DoTrackImport(QObject):
|
||||
session.commit()
|
||||
|
||||
helpers.normalise_track(self.destination_track_path)
|
||||
self.source_model.insert_row(self.next_row_number, track.id, "imported")
|
||||
self.base_model.insert_row(self.next_row_number, track.id, "imported")
|
||||
self.next_row_number += 1
|
||||
|
||||
self.signals.status_message_signal.emit(
|
||||
@ -144,14 +144,19 @@ class FileImporter:
|
||||
Manage importing of files
|
||||
"""
|
||||
|
||||
def __init__(self, active_proxy_model: PlaylistModel, row_number: int) -> None:
|
||||
def __init__(
|
||||
self, base_model: PlaylistModel, row_number: Optional[int] = None
|
||||
) -> None:
|
||||
"""
|
||||
Set up class
|
||||
"""
|
||||
|
||||
# Save parameters
|
||||
self.active_proxy_model = active_proxy_model
|
||||
self.row_number = row_number
|
||||
self.base_model = base_model
|
||||
if row_number:
|
||||
self.row_number = row_number
|
||||
else:
|
||||
self.row_number = base_model.rowCount()
|
||||
# Data structure to track files to import
|
||||
self.import_files_data: list[TrackFileData] = []
|
||||
# Dictionary of exsting tracks
|
||||
@ -279,7 +284,7 @@ class FileImporter:
|
||||
destination_track_path=f.destination_track_path,
|
||||
track_id=f.track_id,
|
||||
audio_metadata=helpers.get_audio_metadata(f.import_file_path),
|
||||
source_model=self.active_proxy_model,
|
||||
base_model=self.base_model,
|
||||
row_number=self.row_number,
|
||||
)
|
||||
|
||||
@ -336,7 +341,7 @@ class FileImporter:
|
||||
f"{self.existing_tracks[track_id].title} "
|
||||
f"({self.existing_tracks[track_id].artist})",
|
||||
track_id,
|
||||
str(self.existing_tracks[track_id].path)
|
||||
str(self.existing_tracks[track_id].path),
|
||||
)
|
||||
)
|
||||
|
||||
@ -448,7 +453,7 @@ class PickMatch(QDialog):
|
||||
self.init_ui(items_with_ids)
|
||||
self.selected_id = -1
|
||||
|
||||
def init_ui(self, items_with_ids: list[tuple[str, int]]) -> None:
|
||||
def init_ui(self, items_with_ids: list[tuple[str, int, str]]) -> None:
|
||||
"""
|
||||
Set up dialog
|
||||
"""
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Standard library imports
|
||||
from dataclasses import dataclass, field
|
||||
from slugify import slugify # type: ignore
|
||||
from typing import List, Optional
|
||||
import argparse
|
||||
import datetime as dt
|
||||
import os
|
||||
from slugify import slugify # type: ignore
|
||||
import subprocess
|
||||
import sys
|
||||
import urllib.parse
|
||||
@ -48,6 +49,7 @@ import stackprinter # type: ignore
|
||||
# App imports
|
||||
from classes import (
|
||||
MusicMusterSignals,
|
||||
Selection,
|
||||
TrackInfo,
|
||||
)
|
||||
from config import Config
|
||||
@ -67,6 +69,27 @@ from utilities import check_db, update_bitrates
|
||||
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)
|
||||
|
||||
|
||||
@dataclass
|
||||
class PlaylistData:
|
||||
base_model: PlaylistModel
|
||||
proxy_model: PlaylistProxyModel
|
||||
|
||||
def __post_init__(self):
|
||||
self.proxy_model.setSourceModel(self.base_model)
|
||||
|
||||
|
||||
class PreviewManager:
|
||||
"""
|
||||
Manage track preview player
|
||||
@ -178,6 +201,52 @@ class PreviewManager:
|
||||
self.start_time = None
|
||||
|
||||
|
||||
class SelectPlaylistDialog(QDialog):
|
||||
def __init__(self, parent=None, playlists=None, session=None):
|
||||
super().__init__()
|
||||
|
||||
if playlists is None:
|
||||
return
|
||||
self.ui = Ui_dlgSelectPlaylist()
|
||||
self.ui.setupUi(self)
|
||||
self.ui.lstPlaylists.itemDoubleClicked.connect(self.list_doubleclick)
|
||||
self.ui.buttonBox.accepted.connect(self.open)
|
||||
self.ui.buttonBox.rejected.connect(self.close)
|
||||
self.session = session
|
||||
self.playlist = None
|
||||
|
||||
record = Settings.get_setting(self.session, "select_playlist_dialog_width")
|
||||
width = record.f_int or 800
|
||||
record = Settings.get_setting(self.session, "select_playlist_dialog_height")
|
||||
height = record.f_int or 600
|
||||
self.resize(width, height)
|
||||
|
||||
for playlist in playlists:
|
||||
p = QListWidgetItem()
|
||||
p.setText(playlist.name)
|
||||
p.setData(Qt.ItemDataRole.UserRole, playlist)
|
||||
self.ui.lstPlaylists.addItem(p)
|
||||
|
||||
def __del__(self): # review
|
||||
record = Settings.get_setting(self.session, "select_playlist_dialog_height")
|
||||
record.f_int = self.height()
|
||||
|
||||
record = Settings.get_setting(self.session, "select_playlist_dialog_width")
|
||||
record.f_int = self.width()
|
||||
|
||||
self.session.commit()
|
||||
|
||||
def list_doubleclick(self, entry): # review
|
||||
self.playlist = entry.data(Qt.ItemDataRole.UserRole)
|
||||
self.accept()
|
||||
|
||||
def open(self): # review
|
||||
if self.ui.lstPlaylists.selectedItems():
|
||||
item = self.ui.lstPlaylists.currentItem()
|
||||
self.playlist = item.data(Qt.ItemDataRole.UserRole)
|
||||
self.accept()
|
||||
|
||||
|
||||
class Window(QMainWindow, Ui_MainWindow):
|
||||
def __init__(
|
||||
self, parent: Optional[QWidget] = None, *args: list, **kwargs: dict
|
||||
@ -205,7 +274,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
self.widgetFadeVolume.setBackground(Config.FADE_CURVE_BACKGROUND)
|
||||
|
||||
self.move_source_rows: Optional[List[int]] = None
|
||||
self.move_source_model: Optional[PlaylistProxyModel] = None
|
||||
self.move_source_model: Optional[PlaylistModel] = None
|
||||
|
||||
self.disable_selection_timing = False
|
||||
self.clock_counter = 0
|
||||
@ -217,6 +286,8 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
self.connect_signals_slots()
|
||||
self.catch_return_key = False
|
||||
self.importer: Optional[FileImporter] = None
|
||||
self.selection = Selection()
|
||||
self.playlists: dict[int, PlaylistData] = {}
|
||||
|
||||
if not Config.USE_INTERNAL_BROWSER:
|
||||
webbrowser.register(
|
||||
@ -256,7 +327,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
def active_tab(self) -> PlaylistTab:
|
||||
return self.tabPlaylist.currentWidget()
|
||||
|
||||
def active_proxy_model(self) -> PlaylistModel:
|
||||
def active_proxy_model(self) -> PlaylistProxyModel:
|
||||
return self.tabPlaylist.currentWidget().model()
|
||||
|
||||
def clear_next(self) -> None:
|
||||
@ -473,15 +544,20 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
|
||||
def create_playlist_tab(self, playlist: Playlists) -> int:
|
||||
"""
|
||||
Take the passed playlist database object, create a playlist tab and
|
||||
Take the passed proxy model, create a playlist tab and
|
||||
add tab to display. Return index number of tab.
|
||||
"""
|
||||
|
||||
log.debug(f"create_playlist_tab({playlist=})")
|
||||
|
||||
# Create model and proxy model
|
||||
self.playlists[playlist.id] = PlaylistData(
|
||||
base_model=PlaylistModel(playlist.id), proxy_model=PlaylistProxyModel()
|
||||
)
|
||||
|
||||
# Create tab
|
||||
playlist_tab = PlaylistTab(
|
||||
musicmuster=self,
|
||||
playlist_id=playlist.id,
|
||||
musicmuster=self, model=self.playlists[playlist.id].proxy_model
|
||||
)
|
||||
idx = self.tabPlaylist.addTab(playlist_tab, playlist.name)
|
||||
|
||||
@ -648,6 +724,13 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
if track_sequence.current:
|
||||
track_sequence.current.fade()
|
||||
|
||||
def get_active_base_model(self) -> PlaylistModel:
|
||||
"""
|
||||
Return the model for the current tab
|
||||
"""
|
||||
|
||||
return self.playlists[self.selection.playlist_id].base_model
|
||||
|
||||
def hide_played(self):
|
||||
"""Toggle hide played tracks"""
|
||||
|
||||
@ -673,7 +756,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
# We need to keep a referent to the FileImporter else it will be
|
||||
# garbage collected while import threads are still running
|
||||
self.importer = FileImporter(
|
||||
self.active_proxy_model(),
|
||||
self.get_active_base_model(),
|
||||
self.active_tab().source_model_selected_row_number(),
|
||||
)
|
||||
self.importer.do_import()
|
||||
@ -681,11 +764,6 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
def insert_header(self) -> None:
|
||||
"""Show dialog box to enter header text and add to playlist"""
|
||||
|
||||
proxy_model = self.active_proxy_model()
|
||||
if proxy_model is None:
|
||||
log.error("No proxy model")
|
||||
return
|
||||
|
||||
# Get header text
|
||||
dlg: QInputDialog = QInputDialog(self)
|
||||
dlg.setInputMode(QInputDialog.InputMode.TextInput)
|
||||
@ -693,7 +771,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
dlg.resize(500, 100)
|
||||
ok = dlg.exec()
|
||||
if ok:
|
||||
proxy_model.insert_row(
|
||||
self.get_active_base_model().insert_row(
|
||||
proposed_row_number=self.active_tab().source_model_selected_row_number(),
|
||||
note=dlg.textValue(),
|
||||
)
|
||||
@ -710,7 +788,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
parent=self,
|
||||
session=session,
|
||||
new_row_number=new_row_number,
|
||||
source_model=self.active_proxy_model(),
|
||||
base_model=self.get_active_base_model(),
|
||||
)
|
||||
dlg.exec()
|
||||
session.commit()
|
||||
@ -722,9 +800,10 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
with db.Session() as session:
|
||||
for playlist in Playlists.get_open(session):
|
||||
if playlist:
|
||||
_ = self.create_playlist_tab(playlist)
|
||||
playlist_ids.append(playlist.id)
|
||||
log.debug(f"load_last_playlists() loaded {playlist=}")
|
||||
# Create tab
|
||||
playlist_ids.append(self.create_playlist_tab(playlist))
|
||||
|
||||
# Set active tab
|
||||
record = Settings.get_setting(session, "active_tab")
|
||||
if record.f_int is not None and record.f_int >= 0:
|
||||
@ -767,7 +846,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
# 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()
|
||||
self.move_source_model = self.get_active_base_model()
|
||||
|
||||
log.debug(
|
||||
f"mark_rows_for_moving(): {self.move_source_rows=} {self.move_source_model=}"
|
||||
@ -804,7 +883,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
to_row = 0
|
||||
|
||||
# Move rows
|
||||
self.active_proxy_model().move_rows_between_playlists(
|
||||
self.get_active_base_model().move_rows_between_playlists(
|
||||
row_numbers, to_row, to_playlist_id
|
||||
)
|
||||
|
||||
@ -834,7 +913,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
Move unplayed rows to another playlist
|
||||
"""
|
||||
|
||||
unplayed_rows = self.active_proxy_model().get_unplayed_rows()
|
||||
unplayed_rows = self.get_active_base_model().get_unplayed_rows()
|
||||
if not unplayed_rows:
|
||||
return
|
||||
# We can get a race condition as selected rows change while
|
||||
@ -917,7 +996,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
if not self.move_source_rows or not self.move_source_model:
|
||||
return
|
||||
|
||||
to_playlist_model: PlaylistModel = self.active_tab().source_model
|
||||
to_playlist_model = self.get_active_base_model()
|
||||
selected_rows = self.active_tab().get_selected_rows()
|
||||
if selected_rows:
|
||||
destination_row = selected_rows[0]
|
||||
@ -934,10 +1013,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
):
|
||||
set_next_row = destination_row
|
||||
|
||||
if (
|
||||
to_playlist_model.playlist_id
|
||||
== self.move_source_model.source_model.playlist_id
|
||||
):
|
||||
if to_playlist_model.playlist_id == self.move_source_model.playlist_id:
|
||||
self.move_source_model.move_rows(self.move_source_rows, destination_row)
|
||||
else:
|
||||
self.move_source_model.move_rows_between_playlists(
|
||||
@ -1064,6 +1140,8 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
)
|
||||
else:
|
||||
return
|
||||
if not track_info:
|
||||
return
|
||||
self.preview_manager.set_track_info(track_info)
|
||||
self.preview_manager.play()
|
||||
else:
|
||||
@ -1096,6 +1174,8 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
if self.preview_manager.is_playing():
|
||||
track_id = self.preview_manager.track_id
|
||||
row_number = self.preview_manager.row_number
|
||||
if not row_number:
|
||||
return
|
||||
with db.Session() as session:
|
||||
track = session.get(Tracks, track_id)
|
||||
if track:
|
||||
@ -1106,8 +1186,8 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
track.intro = intro
|
||||
session.commit()
|
||||
self.preview_manager.set_intro(intro)
|
||||
self.active_tab().source_model.refresh_row(session, row_number)
|
||||
self.active_tab().source_model.invalidate_row(row_number)
|
||||
self.get_active_base_model().refresh_row(session, row_number)
|
||||
self.get_active_base_model().invalidate_row(row_number)
|
||||
|
||||
def preview_start(self) -> None:
|
||||
"""Restart preview"""
|
||||
@ -1294,7 +1374,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
if row_number is None:
|
||||
return None
|
||||
|
||||
track_info = self.active_proxy_model().get_row_info(row_number)
|
||||
track_info = self.get_active_base_model().get_row_info(row_number)
|
||||
if track_info is None:
|
||||
return None
|
||||
|
||||
@ -1385,9 +1465,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
display_row = (
|
||||
self.active_proxy_model()
|
||||
.mapFromSource(
|
||||
self.active_proxy_model().source_model.index(
|
||||
playlist_track.row_number, 0
|
||||
)
|
||||
self.get_active_base_model().index(playlist_track.row_number, 0)
|
||||
)
|
||||
.row()
|
||||
)
|
||||
@ -1430,7 +1508,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
def tab_change(self) -> None:
|
||||
"""Called when active tab changed"""
|
||||
|
||||
self.active_tab().resize_rows()
|
||||
self.active_tab().tab_live()
|
||||
|
||||
def tick_10ms(self) -> None:
|
||||
"""
|
||||
@ -1618,64 +1696,6 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
self.tabPlaylist.setTabIcon(idx, QIcon())
|
||||
|
||||
|
||||
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 SelectPlaylistDialog(QDialog):
|
||||
def __init__(self, parent=None, playlists=None, session=None):
|
||||
super().__init__()
|
||||
|
||||
if playlists is None:
|
||||
return
|
||||
self.ui = Ui_dlgSelectPlaylist()
|
||||
self.ui.setupUi(self)
|
||||
self.ui.lstPlaylists.itemDoubleClicked.connect(self.list_doubleclick)
|
||||
self.ui.buttonBox.accepted.connect(self.open)
|
||||
self.ui.buttonBox.rejected.connect(self.close)
|
||||
self.session = session
|
||||
self.playlist = None
|
||||
|
||||
record = Settings.get_setting(self.session, "select_playlist_dialog_width")
|
||||
width = record.f_int or 800
|
||||
record = Settings.get_setting(self.session, "select_playlist_dialog_height")
|
||||
height = record.f_int or 600
|
||||
self.resize(width, height)
|
||||
|
||||
for playlist in playlists:
|
||||
p = QListWidgetItem()
|
||||
p.setText(playlist.name)
|
||||
p.setData(Qt.ItemDataRole.UserRole, playlist)
|
||||
self.ui.lstPlaylists.addItem(p)
|
||||
|
||||
def __del__(self): # review
|
||||
record = Settings.get_setting(self.session, "select_playlist_dialog_height")
|
||||
record.f_int = self.height()
|
||||
|
||||
record = Settings.get_setting(self.session, "select_playlist_dialog_width")
|
||||
record.f_int = self.width()
|
||||
|
||||
self.session.commit()
|
||||
|
||||
def list_doubleclick(self, entry): # review
|
||||
self.playlist = entry.data(Qt.ItemDataRole.UserRole)
|
||||
self.accept()
|
||||
|
||||
def open(self): # review
|
||||
if self.ui.lstPlaylists.selectedItems():
|
||||
item = self.ui.lstPlaylists.currentItem()
|
||||
self.playlist = item.data(Qt.ItemDataRole.UserRole)
|
||||
self.accept()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
"""
|
||||
If command line arguments given, carry out requested function and
|
||||
|
||||
@ -4,7 +4,7 @@ from __future__ import annotations
|
||||
|
||||
from operator import attrgetter
|
||||
from random import shuffle
|
||||
from typing import Optional
|
||||
from typing import cast, Optional
|
||||
import datetime as dt
|
||||
import re
|
||||
|
||||
@ -1340,8 +1340,9 @@ class PlaylistModel(QAbstractTableModel):
|
||||
unplayed_count += 1
|
||||
duration += row_rat.duration
|
||||
|
||||
# Should never get here
|
||||
return f"Error calculating subtotal ({row_rat.note})"
|
||||
# We should only get here if there were no rows in section (ie,
|
||||
# this was row zero)
|
||||
return Config.SUBTOTAL_ON_ROW_ZERO
|
||||
|
||||
def selection_is_sortable(self, row_numbers: list[int]) -> bool:
|
||||
"""
|
||||
@ -1662,19 +1663,14 @@ class PlaylistProxyModel(QSortFilterProxyModel):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
source_model: PlaylistModel,
|
||||
*args: QObject,
|
||||
**kwargs: QObject,
|
||||
) -> None:
|
||||
self.source_model = source_model
|
||||
super().__init__(*args, **kwargs)
|
||||
super().__init__()
|
||||
|
||||
self.setSourceModel(source_model)
|
||||
# Search all columns
|
||||
self.setFilterKeyColumn(-1)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<PlaylistProxyModel: source_model={self.source_model}>"
|
||||
return f"<PlaylistProxyModel: sourceModel={self.sourceModel}>"
|
||||
|
||||
def filterAcceptsRow(self, source_row: int, source_parent: QModelIndex) -> bool:
|
||||
"""
|
||||
@ -1684,13 +1680,13 @@ class PlaylistProxyModel(QSortFilterProxyModel):
|
||||
if Config.HIDE_PLAYED_MODE != Config.HIDE_PLAYED_MODE_TRACKS:
|
||||
return super().filterAcceptsRow(source_row, source_parent)
|
||||
|
||||
if self.source_model.played_tracks_hidden:
|
||||
if self.source_model.is_played_row(source_row):
|
||||
if self.sourceModel().played_tracks_hidden:
|
||||
if self.sourceModel().is_played_row(source_row):
|
||||
# Don't hide current track
|
||||
if (
|
||||
track_sequence.current
|
||||
and track_sequence.current.playlist_id
|
||||
== self.source_model.playlist_id
|
||||
== self.sourceModel().playlist_id
|
||||
and track_sequence.current.row_number == source_row
|
||||
):
|
||||
return True
|
||||
@ -1698,7 +1694,8 @@ class PlaylistProxyModel(QSortFilterProxyModel):
|
||||
# Don't hide next track
|
||||
if (
|
||||
track_sequence.next
|
||||
and track_sequence.next.playlist_id == self.source_model.playlist_id
|
||||
and track_sequence.next.playlist_id
|
||||
== self.sourceModel().playlist_id
|
||||
and track_sequence.next.row_number == source_row
|
||||
):
|
||||
return True
|
||||
@ -1707,7 +1704,7 @@ class PlaylistProxyModel(QSortFilterProxyModel):
|
||||
if track_sequence.previous:
|
||||
if (
|
||||
track_sequence.previous.playlist_id
|
||||
!= self.source_model.playlist_id
|
||||
!= self.sourceModel().playlist_id
|
||||
or track_sequence.previous.row_number != source_row
|
||||
):
|
||||
# This row isn't our previous track: hide it
|
||||
@ -1731,7 +1728,7 @@ class PlaylistProxyModel(QSortFilterProxyModel):
|
||||
# true next time through.
|
||||
QTimer.singleShot(
|
||||
Config.HIDE_AFTER_PLAYING_OFFSET + 100,
|
||||
lambda: self.source_model.invalidate_row(source_row),
|
||||
lambda: self.sourceModel().invalidate_row(source_row),
|
||||
)
|
||||
return True
|
||||
# Next track not playing yet so don't hide previous
|
||||
@ -1754,105 +1751,9 @@ class PlaylistProxyModel(QSortFilterProxyModel):
|
||||
)
|
||||
)
|
||||
|
||||
# ######################################
|
||||
# Forward functions not handled in proxy
|
||||
# ######################################
|
||||
def sourceModel(self) -> PlaylistModel:
|
||||
"""
|
||||
Override sourceModel to return correct type
|
||||
"""
|
||||
|
||||
def current_track_started(self):
|
||||
return self.source_model.current_track_started()
|
||||
|
||||
def delete_rows(self, row_numbers: list[int]) -> None:
|
||||
return self.source_model.delete_rows(row_numbers)
|
||||
|
||||
def get_duplicate_rows(self) -> list[int]:
|
||||
return self.source_model.get_duplicate_rows()
|
||||
|
||||
def get_rows_duration(self, row_numbers: list[int]) -> int:
|
||||
return self.source_model.get_rows_duration(row_numbers)
|
||||
|
||||
def get_row_info(self, row_number: int) -> RowAndTrack:
|
||||
return self.source_model.get_row_info(row_number)
|
||||
|
||||
def get_row_track_path(self, row_number: int) -> str:
|
||||
return self.source_model.get_row_track_path(row_number)
|
||||
|
||||
def get_unplayed_rows(self) -> list[int]:
|
||||
return self.source_model.get_unplayed_rows()
|
||||
|
||||
def hide_played_tracks(self, hide: bool) -> None:
|
||||
return self.source_model.hide_played_tracks(hide)
|
||||
|
||||
def insert_row(
|
||||
self,
|
||||
proposed_row_number: Optional[int],
|
||||
track_id: Optional[int] = None,
|
||||
note: str = "",
|
||||
) -> None:
|
||||
return self.source_model.insert_row(proposed_row_number, track_id, note)
|
||||
|
||||
def is_header_row(self, row_number: int) -> bool:
|
||||
return self.source_model.is_header_row(row_number)
|
||||
|
||||
def is_played_row(self, row_number: int) -> bool:
|
||||
return self.source_model.is_played_row(row_number)
|
||||
|
||||
def is_track_in_playlist(self, track_id: int) -> Optional[RowAndTrack]:
|
||||
return self.source_model.is_track_in_playlist(track_id)
|
||||
|
||||
def mark_unplayed(self, row_numbers: list[int]) -> None:
|
||||
return self.source_model.mark_unplayed(row_numbers)
|
||||
|
||||
def move_rows(self, from_rows: list[int], to_row_number: int) -> None:
|
||||
return self.source_model.move_rows(from_rows, to_row_number)
|
||||
|
||||
def move_rows_between_playlists(
|
||||
self, from_rows: list[int], to_row_number: int, to_playlist_id: int
|
||||
) -> None:
|
||||
return self.source_model.move_rows_between_playlists(
|
||||
from_rows, to_row_number, to_playlist_id
|
||||
)
|
||||
|
||||
def move_track_add_note(
|
||||
self, new_row_number: int, existing_rat: RowAndTrack, note: str
|
||||
) -> None:
|
||||
return self.source_model.move_track_add_note(new_row_number, existing_rat, note)
|
||||
|
||||
def move_track_to_header(
|
||||
self,
|
||||
header_row_number: int,
|
||||
existing_rat: RowAndTrack,
|
||||
note: Optional[str],
|
||||
) -> None:
|
||||
return self.source_model.move_track_to_header(
|
||||
header_row_number, existing_rat, note
|
||||
)
|
||||
|
||||
def previous_track_ended(self) -> None:
|
||||
return self.source_model.previous_track_ended()
|
||||
|
||||
def remove_track(self, row_number: int) -> None:
|
||||
return self.source_model.remove_track(row_number)
|
||||
|
||||
def rescan_track(self, row_number: int) -> None:
|
||||
return self.source_model.rescan_track(row_number)
|
||||
|
||||
def set_next_row(self, row_number: Optional[int]) -> None:
|
||||
self.source_model.set_next_row(row_number)
|
||||
|
||||
def sort_by_artist(self, row_numbers: list[int]) -> None:
|
||||
return self.source_model.sort_by_artist(row_numbers)
|
||||
|
||||
def sort_by_duration(self, row_numbers: list[int]) -> None:
|
||||
return self.source_model.sort_by_duration(row_numbers)
|
||||
|
||||
def sort_by_lastplayed(self, row_numbers: list[int]) -> None:
|
||||
return self.source_model.sort_by_lastplayed(row_numbers)
|
||||
|
||||
def sort_randomly(self, row_numbers: list[int]) -> None:
|
||||
return self.source_model.sort_randomly(row_numbers)
|
||||
|
||||
def sort_by_title(self, row_numbers: list[int]) -> None:
|
||||
return self.source_model.sort_by_title(row_numbers)
|
||||
|
||||
def update_track_times(self) -> None:
|
||||
return self.source_model.update_track_times()
|
||||
return cast(PlaylistModel, super().sourceModel())
|
||||
|
||||
196
app/playlists.py
196
app/playlists.py
@ -37,7 +37,7 @@ import line_profiler
|
||||
|
||||
# App imports
|
||||
from audacity_controller import AudacityController
|
||||
from classes import ApplicationError, Col, MusicMusterSignals, TrackInfo
|
||||
from classes import ApplicationError, Col, MusicMusterSignals, Selection, TrackInfo
|
||||
from config import Config
|
||||
from dialogs import TrackSelectDialog
|
||||
from helpers import (
|
||||
@ -82,9 +82,9 @@ class PlaylistDelegate(QStyledItemDelegate):
|
||||
|
||||
QTimer.singleShot(0, resize_func)
|
||||
|
||||
def __init__(self, parent: QWidget, source_model: PlaylistModel) -> None:
|
||||
def __init__(self, parent: QWidget, base_model: PlaylistModel) -> None:
|
||||
super().__init__(parent)
|
||||
self.source_model = source_model
|
||||
self.base_model = base_model
|
||||
self.signals = MusicMusterSignals()
|
||||
self.click_position = None
|
||||
self.current_editor: Optional[Any] = None
|
||||
@ -239,7 +239,7 @@ class PlaylistDelegate(QStyledItemDelegate):
|
||||
proxy_model = index.model()
|
||||
edit_index = proxy_model.mapToSource(index)
|
||||
|
||||
self.original_model_data = self.source_model.data(
|
||||
self.original_model_data = self.base_model.data(
|
||||
edit_index, Qt.ItemDataRole.EditRole
|
||||
)
|
||||
if index.column() == Col.INTRO.value:
|
||||
@ -256,7 +256,7 @@ class PlaylistDelegate(QStyledItemDelegate):
|
||||
value = editor.toPlainText().strip()
|
||||
elif isinstance(editor, QDoubleSpinBox):
|
||||
value = editor.value()
|
||||
self.source_model.setData(edit_index, value, Qt.ItemDataRole.EditRole)
|
||||
self.base_model.setData(edit_index, value, Qt.ItemDataRole.EditRole)
|
||||
|
||||
def updateEditorGeometry(self, editor, option, index):
|
||||
editor.setGeometry(option.rect)
|
||||
@ -285,22 +285,17 @@ class PlaylistTab(QTableView):
|
||||
The playlist view
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
musicmuster: "Window",
|
||||
playlist_id: int,
|
||||
) -> None:
|
||||
def __init__(self, musicmuster: "Window", model: PlaylistProxyModel) -> None:
|
||||
super().__init__()
|
||||
|
||||
# Save passed settings
|
||||
self.musicmuster = musicmuster
|
||||
self.playlist_id = playlist_id
|
||||
log.debug(f"PlaylistTab.__init__({playlist_id=})")
|
||||
self.musicmuster = (
|
||||
musicmuster # TODO: do we need to keep a reference to musicmuster?
|
||||
)
|
||||
self.playlist_id = model.sourceModel().playlist_id
|
||||
|
||||
# Set up widget
|
||||
self.source_model = PlaylistModel(playlist_id)
|
||||
self.proxy_model = PlaylistProxyModel(self.source_model)
|
||||
self.setItemDelegate(PlaylistDelegate(self, self.source_model))
|
||||
self.setItemDelegate(PlaylistDelegate(self, model.sourceModel()))
|
||||
self.setAlternatingRowColors(True)
|
||||
self.setVerticalScrollMode(QAbstractItemView.ScrollMode.ScrollPerPixel)
|
||||
self.setDragDropMode(QAbstractItemView.DragDropMode.InternalMove)
|
||||
@ -328,9 +323,8 @@ class PlaylistTab(QTableView):
|
||||
self.setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection)
|
||||
self.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
|
||||
|
||||
# Load playlist rows
|
||||
self.setModel(self.proxy_model)
|
||||
self._set_column_widths()
|
||||
# Singleton object to store selection
|
||||
self.selection = Selection()
|
||||
|
||||
# Set up for Audacity
|
||||
try:
|
||||
@ -339,6 +333,10 @@ class PlaylistTab(QTableView):
|
||||
self.ac = None
|
||||
show_warning(self.musicmuster, "Audacity error", str(e))
|
||||
|
||||
# Load model, set column widths
|
||||
self.setModel(model)
|
||||
self._set_column_widths()
|
||||
|
||||
# Stretch last column *after* setting column widths which is
|
||||
# *much* faster
|
||||
h_header = self.horizontalHeader()
|
||||
@ -373,7 +371,7 @@ class PlaylistTab(QTableView):
|
||||
|
||||
# Update start times in case a start time in a note has been
|
||||
# edited
|
||||
self.source_model.update_track_times()
|
||||
self.get_base_model().update_track_times()
|
||||
|
||||
# Deselect edited line
|
||||
self.clear_selection()
|
||||
@ -382,36 +380,50 @@ class PlaylistTab(QTableView):
|
||||
def dropEvent(
|
||||
self, event: Optional[QDropEvent], dummy_for_profiling: Optional[int] = None
|
||||
) -> None:
|
||||
"""
|
||||
Move dropped rows
|
||||
"""
|
||||
|
||||
if not event:
|
||||
return
|
||||
|
||||
if event.source() is not self or (
|
||||
event.dropAction() != Qt.DropAction.MoveAction
|
||||
and self.dragDropMode() != QAbstractItemView.DragDropMode.InternalMove
|
||||
):
|
||||
super().dropEvent(event)
|
||||
return super().dropEvent(event)
|
||||
|
||||
from_rows = self.selected_model_row_numbers()
|
||||
to_index = self.indexAt(event.position().toPoint())
|
||||
|
||||
# The drop indicator can either be immediately below a row or
|
||||
# immediately above a row. There's about a 1 pixel difference,
|
||||
# but we always want to drop between rows regardless of where
|
||||
# drop indicator is.
|
||||
if (
|
||||
self.dropIndicatorPosition()
|
||||
== QAbstractItemView.DropIndicatorPosition.BelowItem
|
||||
):
|
||||
proxy_index = self.proxy_model.createIndex(
|
||||
to_index.row() + 1,
|
||||
to_index.column(),
|
||||
to_index.internalId(),
|
||||
)
|
||||
# Drop on the row below
|
||||
next_row = to_index.row() + 1
|
||||
if next_row < self.model().rowCount(): # Ensure the row exists
|
||||
destination_index = to_index.siblingAtRow(next_row)
|
||||
else:
|
||||
# Handle edge case where next_row is beyond the last row
|
||||
destination_index = to_index
|
||||
else:
|
||||
proxy_index = to_index
|
||||
to_model_row = self.proxy_model.mapToSource(proxy_index).row()
|
||||
destination_index = to_index
|
||||
|
||||
to_model_row = self.model().mapToSource(destination_index).row()
|
||||
log.debug(
|
||||
f"PlaylistTab.dropEvent(): {from_rows=}, {proxy_index=}, {to_model_row=}"
|
||||
f"PlaylistTab.dropEvent(): {from_rows=}, {destination_index=}, {to_model_row=}"
|
||||
)
|
||||
|
||||
# Sanity check
|
||||
base_model_row_count = self.get_base_model().rowCount()
|
||||
if (
|
||||
0 <= min(from_rows) <= self.source_model.rowCount()
|
||||
and 0 <= max(from_rows) <= self.source_model.rowCount()
|
||||
and 0 <= to_model_row <= self.source_model.rowCount()
|
||||
0 <= min(from_rows) <= base_model_row_count
|
||||
and 0 <= to_model_row <= base_model_row_count
|
||||
):
|
||||
# If we move a row to immediately under the current track, make
|
||||
# that moved row the next track
|
||||
@ -422,7 +434,7 @@ class PlaylistTab(QTableView):
|
||||
):
|
||||
set_next_row = to_model_row
|
||||
|
||||
self.source_model.move_rows(from_rows, to_model_row)
|
||||
self.get_base_model().move_rows(from_rows, to_model_row)
|
||||
|
||||
# Reset drag mode to allow row selection by dragging
|
||||
self.setDragEnabled(False)
|
||||
@ -435,7 +447,7 @@ class PlaylistTab(QTableView):
|
||||
|
||||
# Set next row if we are immediately under current row
|
||||
if set_next_row:
|
||||
self.source_model.set_next_row(set_next_row)
|
||||
self.get_base_model().set_next_row(set_next_row)
|
||||
|
||||
event.accept()
|
||||
|
||||
@ -469,12 +481,14 @@ class PlaylistTab(QTableView):
|
||||
"""
|
||||
|
||||
selected_rows = self.get_selected_rows()
|
||||
self.selection.rows = selected_rows
|
||||
|
||||
# If no rows are selected, we have nothing to do
|
||||
if len(selected_rows) == 0:
|
||||
self.musicmuster.lblSumPlaytime.setText("")
|
||||
else:
|
||||
if not self.musicmuster.disable_selection_timing:
|
||||
selected_duration = self.source_model.get_rows_duration(
|
||||
selected_duration = self.get_base_model().get_rows_duration(
|
||||
self.get_selected_rows()
|
||||
)
|
||||
if selected_duration > 0:
|
||||
@ -525,7 +539,7 @@ class PlaylistTab(QTableView):
|
||||
parent=self.musicmuster,
|
||||
session=session,
|
||||
new_row_number=model_row_number,
|
||||
source_model=self.source_model,
|
||||
base_model=self.get_base_model(),
|
||||
add_to_header=True,
|
||||
)
|
||||
dlg.exec()
|
||||
@ -535,12 +549,12 @@ class PlaylistTab(QTableView):
|
||||
"""Used to process context (right-click) menu, which is defined here"""
|
||||
|
||||
self.menu.clear()
|
||||
proxy_model = self.proxy_model
|
||||
|
||||
index = proxy_model.index(item.row(), item.column())
|
||||
model_row_number = proxy_model.mapToSource(index).row()
|
||||
index = self.model().index(item.row(), item.column())
|
||||
model_row_number = self.model().mapToSource(index).row()
|
||||
base_model = self.get_base_model()
|
||||
|
||||
header_row = proxy_model.is_header_row(model_row_number)
|
||||
header_row = self.get_base_model().is_header_row(model_row_number)
|
||||
track_row = not header_row
|
||||
if track_sequence.current:
|
||||
this_is_current_row = model_row_number == track_sequence.current.row_number
|
||||
@ -550,7 +564,7 @@ class PlaylistTab(QTableView):
|
||||
this_is_next_row = model_row_number == track_sequence.next.row_number
|
||||
else:
|
||||
this_is_next_row = False
|
||||
track_path = self.source_model.get_row_info(model_row_number).path
|
||||
track_path = base_model.get_row_info(model_row_number).path
|
||||
|
||||
# Open/import in/from Audacity
|
||||
if track_row and not this_is_current_row:
|
||||
@ -591,7 +605,7 @@ class PlaylistTab(QTableView):
|
||||
if track_row and not this_is_current_row and not this_is_next_row:
|
||||
self._add_context_menu(
|
||||
"Remove track from row",
|
||||
lambda: proxy_model.remove_track(model_row_number),
|
||||
lambda: base_model.remove_track(model_row_number),
|
||||
)
|
||||
|
||||
# Remove comments
|
||||
@ -605,7 +619,7 @@ class PlaylistTab(QTableView):
|
||||
self.menu.addSeparator()
|
||||
|
||||
# Mark unplayed
|
||||
if track_row and proxy_model.is_played_row(model_row_number):
|
||||
if track_row and base_model.is_played_row(model_row_number):
|
||||
self._add_context_menu(
|
||||
"Mark unplayed",
|
||||
lambda: self._mark_as_unplayed(self.get_selected_rows()),
|
||||
@ -624,27 +638,27 @@ class PlaylistTab(QTableView):
|
||||
sort_menu = self.menu.addMenu("Sort")
|
||||
self._add_context_menu(
|
||||
"by title",
|
||||
lambda: proxy_model.sort_by_title(self.get_selected_rows()),
|
||||
lambda: base_model.sort_by_title(self.get_selected_rows()),
|
||||
parent_menu=sort_menu,
|
||||
)
|
||||
self._add_context_menu(
|
||||
"by artist",
|
||||
lambda: proxy_model.sort_by_artist(self.get_selected_rows()),
|
||||
lambda: base_model.sort_by_artist(self.get_selected_rows()),
|
||||
parent_menu=sort_menu,
|
||||
)
|
||||
self._add_context_menu(
|
||||
"by duration",
|
||||
lambda: proxy_model.sort_by_duration(self.get_selected_rows()),
|
||||
lambda: base_model.sort_by_duration(self.get_selected_rows()),
|
||||
parent_menu=sort_menu,
|
||||
)
|
||||
self._add_context_menu(
|
||||
"by last played",
|
||||
lambda: proxy_model.sort_by_lastplayed(self.get_selected_rows()),
|
||||
lambda: base_model.sort_by_lastplayed(self.get_selected_rows()),
|
||||
parent_menu=sort_menu,
|
||||
)
|
||||
self._add_context_menu(
|
||||
"randomly",
|
||||
lambda: proxy_model.sort_randomly(self.get_selected_rows()),
|
||||
lambda: base_model.sort_randomly(self.get_selected_rows()),
|
||||
parent_menu=sort_menu,
|
||||
)
|
||||
|
||||
@ -711,7 +725,7 @@ class PlaylistTab(QTableView):
|
||||
to the clipboard. Otherwise, return None.
|
||||
"""
|
||||
|
||||
track_path = self.source_model.get_row_info(row_number).path
|
||||
track_path = self.get_base_model().get_row_info(row_number).path
|
||||
if not track_path:
|
||||
return
|
||||
|
||||
@ -734,7 +748,7 @@ class PlaylistTab(QTableView):
|
||||
Called when track starts playing
|
||||
"""
|
||||
|
||||
self.source_model.current_track_started()
|
||||
self.get_base_model().current_track_started()
|
||||
# Scroll to current section if hide mode is by section
|
||||
if (
|
||||
self.musicmuster.hide_played_tracks
|
||||
@ -766,9 +780,18 @@ class PlaylistTab(QTableView):
|
||||
if not ask_yes_no("Delete rows", f"Really delete {row_count} row{plural}?"):
|
||||
return
|
||||
|
||||
self.source_model.delete_rows(self.selected_model_row_numbers())
|
||||
base_model = self.get_base_model()
|
||||
|
||||
base_model.delete_rows(self.selected_model_row_numbers())
|
||||
self.clear_selection()
|
||||
|
||||
def get_base_model(self) -> PlaylistModel:
|
||||
"""
|
||||
Return the base model for this proxy model
|
||||
"""
|
||||
|
||||
return cast(PlaylistModel, self.model().sourceModel())
|
||||
|
||||
def get_selected_row_track_info(self) -> Optional[TrackInfo]:
|
||||
"""
|
||||
Return the track_id and row number of the selected
|
||||
@ -780,11 +803,13 @@ class PlaylistTab(QTableView):
|
||||
if selected_row is None:
|
||||
return None
|
||||
|
||||
base_model = self.get_base_model()
|
||||
model_row_number = self.source_model_selected_row_number()
|
||||
|
||||
if model_row_number is None:
|
||||
return None
|
||||
else:
|
||||
track_id = self.source_model.get_row_track_id(model_row_number)
|
||||
track_id = base_model.get_row_track_id(model_row_number)
|
||||
if not track_id:
|
||||
return None
|
||||
else:
|
||||
@ -808,12 +833,7 @@ class PlaylistTab(QTableView):
|
||||
# items in that row selected)
|
||||
result = sorted(
|
||||
list(
|
||||
set(
|
||||
[
|
||||
self.proxy_model.mapToSource(a).row()
|
||||
for a in self.selectedIndexes()
|
||||
]
|
||||
)
|
||||
set([self.model().mapToSource(a).row() for a in self.selectedIndexes()])
|
||||
)
|
||||
)
|
||||
|
||||
@ -825,7 +845,7 @@ class PlaylistTab(QTableView):
|
||||
Scroll played sections off screen
|
||||
"""
|
||||
|
||||
self.scroll_to_top(self.source_model.active_section_header())
|
||||
self.scroll_to_top(self.get_base_model().active_section_header())
|
||||
|
||||
def _import_from_audacity(self, row_number: int) -> None:
|
||||
"""
|
||||
@ -844,7 +864,7 @@ class PlaylistTab(QTableView):
|
||||
def _info_row(self, row_number: int) -> None:
|
||||
"""Display popup with info re row"""
|
||||
|
||||
prd = self.source_model.get_row_info(row_number)
|
||||
prd = self.get_base_model().get_row_info(row_number)
|
||||
if prd:
|
||||
txt = (
|
||||
f"Title: {prd.title}\n"
|
||||
@ -863,7 +883,7 @@ class PlaylistTab(QTableView):
|
||||
def _mark_as_unplayed(self, row_numbers: List[int]) -> None:
|
||||
"""Mark row as unplayed"""
|
||||
|
||||
self.source_model.mark_unplayed(row_numbers)
|
||||
self.get_base_model().mark_unplayed(row_numbers)
|
||||
self.clear_selection()
|
||||
|
||||
def _mark_for_moving(self) -> None:
|
||||
@ -873,6 +893,13 @@ class PlaylistTab(QTableView):
|
||||
|
||||
self.musicmuster.mark_rows_for_moving()
|
||||
|
||||
def model(self) -> PlaylistProxyModel:
|
||||
"""
|
||||
Override return type to keep mypy happy in this module
|
||||
"""
|
||||
|
||||
return cast(PlaylistProxyModel, super().model())
|
||||
|
||||
def _move_selected_rows(self) -> None:
|
||||
"""
|
||||
Move selected rows here
|
||||
@ -885,7 +912,7 @@ class PlaylistTab(QTableView):
|
||||
Open track in passed row in Audacity
|
||||
"""
|
||||
|
||||
path = self.source_model.get_row_track_path(row_number)
|
||||
path = self.get_base_model().get_row_track_path(row_number)
|
||||
if not path:
|
||||
log.error(f"_open_in_audacity: can't get path for {row_number=}")
|
||||
return
|
||||
@ -903,7 +930,7 @@ class PlaylistTab(QTableView):
|
||||
"""
|
||||
|
||||
# Let the model know
|
||||
self.source_model.previous_track_ended()
|
||||
self.get_base_model().previous_track_ended()
|
||||
|
||||
def _remove_comments(self) -> None:
|
||||
"""
|
||||
@ -914,12 +941,12 @@ class PlaylistTab(QTableView):
|
||||
if not row_numbers:
|
||||
return
|
||||
|
||||
self.source_model.remove_comments(row_numbers)
|
||||
self.get_base_model().remove_comments(row_numbers)
|
||||
|
||||
def _rescan(self, row_number: int) -> None:
|
||||
"""Rescan track"""
|
||||
|
||||
self.source_model.rescan_track(row_number)
|
||||
self.get_base_model().rescan_track(row_number)
|
||||
self.clear_selection()
|
||||
|
||||
def resize_rows(self, playlist_id: Optional[int] = None) -> None:
|
||||
@ -934,7 +961,7 @@ class PlaylistTab(QTableView):
|
||||
|
||||
# Suggestion from phind.com
|
||||
def resize_row(row, count=1):
|
||||
row_count = self.source_model.rowCount()
|
||||
row_count = self.model().rowCount()
|
||||
for todo in range(count):
|
||||
if row < row_count:
|
||||
self.resizeRowToContents(row)
|
||||
@ -953,7 +980,7 @@ class PlaylistTab(QTableView):
|
||||
if row_number is None:
|
||||
return
|
||||
|
||||
row_index = self.proxy_model.index(row_number, 0)
|
||||
row_index = self.model().index(row_number, 0)
|
||||
self.scrollTo(row_index, QAbstractItemView.ScrollHint.PositionAtTop)
|
||||
|
||||
def select_duplicate_rows(self) -> None:
|
||||
@ -968,7 +995,7 @@ class PlaylistTab(QTableView):
|
||||
# We need to be in MultiSelection mode
|
||||
self.setSelectionMode(QAbstractItemView.SelectionMode.MultiSelection)
|
||||
# Get the duplicate rows
|
||||
duplicate_rows = self.source_model.get_duplicate_rows()
|
||||
duplicate_rows = self.get_base_model().get_duplicate_rows()
|
||||
# Select the rows
|
||||
for duplicate_row in duplicate_rows:
|
||||
self.selectRow(duplicate_row)
|
||||
@ -983,7 +1010,7 @@ class PlaylistTab(QTableView):
|
||||
selected_index = self._selected_row_index()
|
||||
if selected_index is None:
|
||||
return None
|
||||
return self.proxy_model.mapToSource(selected_index).row()
|
||||
return self.model().mapToSource(selected_index).row()
|
||||
|
||||
def selected_model_row_numbers(self) -> List[int]:
|
||||
"""
|
||||
@ -994,9 +1021,8 @@ class PlaylistTab(QTableView):
|
||||
selected_indexes = self._selected_row_indexes()
|
||||
if selected_indexes is None:
|
||||
return []
|
||||
if hasattr(self.proxy_model, "mapToSource"):
|
||||
return [self.proxy_model.mapToSource(a).row() for a in selected_indexes]
|
||||
return [a.row() for a in selected_indexes]
|
||||
|
||||
return [self.model().mapToSource(a).row() for a in selected_indexes]
|
||||
|
||||
def _selected_row_index(self) -> Optional[QModelIndex]:
|
||||
"""
|
||||
@ -1053,7 +1079,7 @@ class PlaylistTab(QTableView):
|
||||
log.debug(f"set_row_as_next_track() {model_row_number=}")
|
||||
if model_row_number is None:
|
||||
return
|
||||
self.source_model.set_next_row(model_row_number)
|
||||
self.get_base_model().set_next_row(model_row_number)
|
||||
self.clearSelection()
|
||||
|
||||
def _span_cells(
|
||||
@ -1061,17 +1087,19 @@ class PlaylistTab(QTableView):
|
||||
) -> None:
|
||||
"""
|
||||
Implement spanning of cells, initiated by signal
|
||||
|
||||
row and column are from the base model so we need to translate
|
||||
the row into this display row
|
||||
"""
|
||||
|
||||
if playlist_id != self.playlist_id:
|
||||
return
|
||||
|
||||
proxy_model = self.proxy_model
|
||||
edit_index = proxy_model.mapFromSource(
|
||||
self.source_model.createIndex(row, column)
|
||||
)
|
||||
row = edit_index.row()
|
||||
column = edit_index.column()
|
||||
base_model = self.get_base_model()
|
||||
|
||||
cell_index = self.model().mapFromSource(base_model.createIndex(row, column))
|
||||
row = cell_index.row()
|
||||
column = cell_index.column()
|
||||
|
||||
# Don't set spanning if already in place because that is seen as
|
||||
# a change to the view and thus it refreshes the data which
|
||||
@ -1084,6 +1112,16 @@ class PlaylistTab(QTableView):
|
||||
|
||||
self.setSpan(row, column, rowSpan, columnSpan)
|
||||
|
||||
def tab_live(self) -> None:
|
||||
"""
|
||||
Called when tab gets focus
|
||||
"""
|
||||
|
||||
self.selection.playlist_id = self.playlist_id
|
||||
self.selection.rows = self.get_selected_rows()
|
||||
|
||||
self.resize_rows()
|
||||
|
||||
def _unmark_as_next(self) -> None:
|
||||
"""Rescan track"""
|
||||
|
||||
|
||||
@ -15,7 +15,11 @@ class Ui_MainWindow(object):
|
||||
MainWindow.resize(1280, 857)
|
||||
MainWindow.setMinimumSize(QtCore.QSize(1280, 0))
|
||||
icon = QtGui.QIcon()
|
||||
icon.addPixmap(QtGui.QPixmap(":/icons/musicmuster"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||
icon.addPixmap(
|
||||
QtGui.QPixmap(":/icons/musicmuster"),
|
||||
QtGui.QIcon.Mode.Normal,
|
||||
QtGui.QIcon.State.Off,
|
||||
)
|
||||
MainWindow.setWindowIcon(icon)
|
||||
MainWindow.setStyleSheet("")
|
||||
self.centralwidget = QtWidgets.QWidget(parent=MainWindow)
|
||||
@ -27,39 +31,62 @@ class Ui_MainWindow(object):
|
||||
self.verticalLayout_3 = QtWidgets.QVBoxLayout()
|
||||
self.verticalLayout_3.setObjectName("verticalLayout_3")
|
||||
self.previous_track_2 = QtWidgets.QLabel(parent=self.centralwidget)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred)
|
||||
sizePolicy = QtWidgets.QSizePolicy(
|
||||
QtWidgets.QSizePolicy.Policy.Preferred,
|
||||
QtWidgets.QSizePolicy.Policy.Preferred,
|
||||
)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.previous_track_2.sizePolicy().hasHeightForWidth())
|
||||
sizePolicy.setHeightForWidth(
|
||||
self.previous_track_2.sizePolicy().hasHeightForWidth()
|
||||
)
|
||||
self.previous_track_2.setSizePolicy(sizePolicy)
|
||||
self.previous_track_2.setMaximumSize(QtCore.QSize(230, 16777215))
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Sans")
|
||||
font.setPointSize(20)
|
||||
self.previous_track_2.setFont(font)
|
||||
self.previous_track_2.setStyleSheet("background-color: #f8d7da;\n"
|
||||
"border: 1px solid rgb(85, 87, 83);")
|
||||
self.previous_track_2.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter)
|
||||
self.previous_track_2.setStyleSheet(
|
||||
"background-color: #f8d7da;\n" "border: 1px solid rgb(85, 87, 83);"
|
||||
)
|
||||
self.previous_track_2.setAlignment(
|
||||
QtCore.Qt.AlignmentFlag.AlignRight
|
||||
| QtCore.Qt.AlignmentFlag.AlignTrailing
|
||||
| QtCore.Qt.AlignmentFlag.AlignVCenter
|
||||
)
|
||||
self.previous_track_2.setObjectName("previous_track_2")
|
||||
self.verticalLayout_3.addWidget(self.previous_track_2)
|
||||
self.current_track_2 = QtWidgets.QLabel(parent=self.centralwidget)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred)
|
||||
sizePolicy = QtWidgets.QSizePolicy(
|
||||
QtWidgets.QSizePolicy.Policy.Preferred,
|
||||
QtWidgets.QSizePolicy.Policy.Preferred,
|
||||
)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.current_track_2.sizePolicy().hasHeightForWidth())
|
||||
sizePolicy.setHeightForWidth(
|
||||
self.current_track_2.sizePolicy().hasHeightForWidth()
|
||||
)
|
||||
self.current_track_2.setSizePolicy(sizePolicy)
|
||||
self.current_track_2.setMaximumSize(QtCore.QSize(230, 16777215))
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Sans")
|
||||
font.setPointSize(20)
|
||||
self.current_track_2.setFont(font)
|
||||
self.current_track_2.setStyleSheet("background-color: #d4edda;\n"
|
||||
"border: 1px solid rgb(85, 87, 83);")
|
||||
self.current_track_2.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter)
|
||||
self.current_track_2.setStyleSheet(
|
||||
"background-color: #d4edda;\n" "border: 1px solid rgb(85, 87, 83);"
|
||||
)
|
||||
self.current_track_2.setAlignment(
|
||||
QtCore.Qt.AlignmentFlag.AlignRight
|
||||
| QtCore.Qt.AlignmentFlag.AlignTrailing
|
||||
| QtCore.Qt.AlignmentFlag.AlignVCenter
|
||||
)
|
||||
self.current_track_2.setObjectName("current_track_2")
|
||||
self.verticalLayout_3.addWidget(self.current_track_2)
|
||||
self.next_track_2 = QtWidgets.QLabel(parent=self.centralwidget)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred)
|
||||
sizePolicy = QtWidgets.QSizePolicy(
|
||||
QtWidgets.QSizePolicy.Policy.Preferred,
|
||||
QtWidgets.QSizePolicy.Policy.Preferred,
|
||||
)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.next_track_2.sizePolicy().hasHeightForWidth())
|
||||
@ -69,19 +96,29 @@ class Ui_MainWindow(object):
|
||||
font.setFamily("Sans")
|
||||
font.setPointSize(20)
|
||||
self.next_track_2.setFont(font)
|
||||
self.next_track_2.setStyleSheet("background-color: #fff3cd;\n"
|
||||
"border: 1px solid rgb(85, 87, 83);")
|
||||
self.next_track_2.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter)
|
||||
self.next_track_2.setStyleSheet(
|
||||
"background-color: #fff3cd;\n" "border: 1px solid rgb(85, 87, 83);"
|
||||
)
|
||||
self.next_track_2.setAlignment(
|
||||
QtCore.Qt.AlignmentFlag.AlignRight
|
||||
| QtCore.Qt.AlignmentFlag.AlignTrailing
|
||||
| QtCore.Qt.AlignmentFlag.AlignVCenter
|
||||
)
|
||||
self.next_track_2.setObjectName("next_track_2")
|
||||
self.verticalLayout_3.addWidget(self.next_track_2)
|
||||
self.horizontalLayout_3.addLayout(self.verticalLayout_3)
|
||||
self.verticalLayout = QtWidgets.QVBoxLayout()
|
||||
self.verticalLayout.setObjectName("verticalLayout")
|
||||
self.hdrPreviousTrack = QtWidgets.QLabel(parent=self.centralwidget)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred)
|
||||
sizePolicy = QtWidgets.QSizePolicy(
|
||||
QtWidgets.QSizePolicy.Policy.Preferred,
|
||||
QtWidgets.QSizePolicy.Policy.Preferred,
|
||||
)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.hdrPreviousTrack.sizePolicy().hasHeightForWidth())
|
||||
sizePolicy.setHeightForWidth(
|
||||
self.hdrPreviousTrack.sizePolicy().hasHeightForWidth()
|
||||
)
|
||||
self.hdrPreviousTrack.setSizePolicy(sizePolicy)
|
||||
self.hdrPreviousTrack.setMinimumSize(QtCore.QSize(0, 0))
|
||||
self.hdrPreviousTrack.setMaximumSize(QtCore.QSize(16777215, 16777215))
|
||||
@ -89,32 +126,43 @@ class Ui_MainWindow(object):
|
||||
font.setFamily("Sans")
|
||||
font.setPointSize(20)
|
||||
self.hdrPreviousTrack.setFont(font)
|
||||
self.hdrPreviousTrack.setStyleSheet("background-color: #f8d7da;\n"
|
||||
"border: 1px solid rgb(85, 87, 83);")
|
||||
self.hdrPreviousTrack.setStyleSheet(
|
||||
"background-color: #f8d7da;\n" "border: 1px solid rgb(85, 87, 83);"
|
||||
)
|
||||
self.hdrPreviousTrack.setText("")
|
||||
self.hdrPreviousTrack.setWordWrap(False)
|
||||
self.hdrPreviousTrack.setObjectName("hdrPreviousTrack")
|
||||
self.verticalLayout.addWidget(self.hdrPreviousTrack)
|
||||
self.hdrCurrentTrack = QtWidgets.QPushButton(parent=self.centralwidget)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred)
|
||||
sizePolicy = QtWidgets.QSizePolicy(
|
||||
QtWidgets.QSizePolicy.Policy.Preferred,
|
||||
QtWidgets.QSizePolicy.Policy.Preferred,
|
||||
)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.hdrCurrentTrack.sizePolicy().hasHeightForWidth())
|
||||
sizePolicy.setHeightForWidth(
|
||||
self.hdrCurrentTrack.sizePolicy().hasHeightForWidth()
|
||||
)
|
||||
self.hdrCurrentTrack.setSizePolicy(sizePolicy)
|
||||
font = QtGui.QFont()
|
||||
font.setPointSize(20)
|
||||
self.hdrCurrentTrack.setFont(font)
|
||||
self.hdrCurrentTrack.setStyleSheet("background-color: #d4edda;\n"
|
||||
"border: 1px solid rgb(85, 87, 83);\n"
|
||||
"text-align: left;\n"
|
||||
"padding-left: 8px;\n"
|
||||
"")
|
||||
self.hdrCurrentTrack.setStyleSheet(
|
||||
"background-color: #d4edda;\n"
|
||||
"border: 1px solid rgb(85, 87, 83);\n"
|
||||
"text-align: left;\n"
|
||||
"padding-left: 8px;\n"
|
||||
""
|
||||
)
|
||||
self.hdrCurrentTrack.setText("")
|
||||
self.hdrCurrentTrack.setFlat(True)
|
||||
self.hdrCurrentTrack.setObjectName("hdrCurrentTrack")
|
||||
self.verticalLayout.addWidget(self.hdrCurrentTrack)
|
||||
self.hdrNextTrack = QtWidgets.QPushButton(parent=self.centralwidget)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred)
|
||||
sizePolicy = QtWidgets.QSizePolicy(
|
||||
QtWidgets.QSizePolicy.Policy.Preferred,
|
||||
QtWidgets.QSizePolicy.Policy.Preferred,
|
||||
)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.hdrNextTrack.sizePolicy().hasHeightForWidth())
|
||||
@ -122,10 +170,12 @@ class Ui_MainWindow(object):
|
||||
font = QtGui.QFont()
|
||||
font.setPointSize(20)
|
||||
self.hdrNextTrack.setFont(font)
|
||||
self.hdrNextTrack.setStyleSheet("background-color: #fff3cd;\n"
|
||||
"border: 1px solid rgb(85, 87, 83);\n"
|
||||
"text-align: left;\n"
|
||||
"padding-left: 8px;")
|
||||
self.hdrNextTrack.setStyleSheet(
|
||||
"background-color: #fff3cd;\n"
|
||||
"border: 1px solid rgb(85, 87, 83);\n"
|
||||
"text-align: left;\n"
|
||||
"padding-left: 8px;"
|
||||
)
|
||||
self.hdrNextTrack.setText("")
|
||||
self.hdrNextTrack.setFlat(True)
|
||||
self.hdrNextTrack.setObjectName("hdrNextTrack")
|
||||
@ -172,7 +222,12 @@ class Ui_MainWindow(object):
|
||||
self.cartsWidget.setObjectName("cartsWidget")
|
||||
self.horizontalLayout_Carts = QtWidgets.QHBoxLayout(self.cartsWidget)
|
||||
self.horizontalLayout_Carts.setObjectName("horizontalLayout_Carts")
|
||||
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
|
||||
spacerItem = QtWidgets.QSpacerItem(
|
||||
40,
|
||||
20,
|
||||
QtWidgets.QSizePolicy.Policy.Expanding,
|
||||
QtWidgets.QSizePolicy.Policy.Minimum,
|
||||
)
|
||||
self.horizontalLayout_Carts.addItem(spacerItem)
|
||||
self.gridLayout_4.addWidget(self.cartsWidget, 2, 0, 1, 1)
|
||||
self.frame_6 = QtWidgets.QFrame(parent=self.centralwidget)
|
||||
@ -217,7 +272,11 @@ class Ui_MainWindow(object):
|
||||
self.btnPreview = QtWidgets.QPushButton(parent=self.FadeStopInfoFrame)
|
||||
self.btnPreview.setMinimumSize(QtCore.QSize(132, 41))
|
||||
icon1 = QtGui.QIcon()
|
||||
icon1.addPixmap(QtGui.QPixmap(":/icons/headphones"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||
icon1.addPixmap(
|
||||
QtGui.QPixmap(":/icons/headphones"),
|
||||
QtGui.QIcon.Mode.Normal,
|
||||
QtGui.QIcon.State.Off,
|
||||
)
|
||||
self.btnPreview.setIcon(icon1)
|
||||
self.btnPreview.setIconSize(QtCore.QSize(30, 30))
|
||||
self.btnPreview.setCheckable(True)
|
||||
@ -239,8 +298,16 @@ class Ui_MainWindow(object):
|
||||
self.btnPreviewArm.setMaximumSize(QtCore.QSize(44, 23))
|
||||
self.btnPreviewArm.setText("")
|
||||
icon2 = QtGui.QIcon()
|
||||
icon2.addPixmap(QtGui.QPixmap(":/icons/record-button.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||
icon2.addPixmap(QtGui.QPixmap(":/icons/record-red-button.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.On)
|
||||
icon2.addPixmap(
|
||||
QtGui.QPixmap(":/icons/record-button.png"),
|
||||
QtGui.QIcon.Mode.Normal,
|
||||
QtGui.QIcon.State.Off,
|
||||
)
|
||||
icon2.addPixmap(
|
||||
QtGui.QPixmap(":/icons/record-red-button.png"),
|
||||
QtGui.QIcon.Mode.Normal,
|
||||
QtGui.QIcon.State.On,
|
||||
)
|
||||
self.btnPreviewArm.setIcon(icon2)
|
||||
self.btnPreviewArm.setCheckable(True)
|
||||
self.btnPreviewArm.setObjectName("btnPreviewArm")
|
||||
@ -261,8 +328,16 @@ class Ui_MainWindow(object):
|
||||
self.btnPreviewMark.setMaximumSize(QtCore.QSize(44, 23))
|
||||
self.btnPreviewMark.setText("")
|
||||
icon3 = QtGui.QIcon()
|
||||
icon3.addPixmap(QtGui.QPixmap(":/icons/star.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.On)
|
||||
icon3.addPixmap(QtGui.QPixmap(":/icons/star_empty.png"), QtGui.QIcon.Mode.Disabled, QtGui.QIcon.State.Off)
|
||||
icon3.addPixmap(
|
||||
QtGui.QPixmap(":/icons/star.png"),
|
||||
QtGui.QIcon.Mode.Normal,
|
||||
QtGui.QIcon.State.On,
|
||||
)
|
||||
icon3.addPixmap(
|
||||
QtGui.QPixmap(":/icons/star_empty.png"),
|
||||
QtGui.QIcon.Mode.Disabled,
|
||||
QtGui.QIcon.State.Off,
|
||||
)
|
||||
self.btnPreviewMark.setIcon(icon3)
|
||||
self.btnPreviewMark.setObjectName("btnPreviewMark")
|
||||
self.btnPreviewFwd = QtWidgets.QPushButton(parent=self.groupBoxIntroControls)
|
||||
@ -363,10 +438,15 @@ class Ui_MainWindow(object):
|
||||
self.verticalLayout_7.addWidget(self.label_silent_timer)
|
||||
self.horizontalLayout.addWidget(self.frame_silent)
|
||||
self.widgetFadeVolume = PlotWidget(parent=self.InfoFooterFrame)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred)
|
||||
sizePolicy = QtWidgets.QSizePolicy(
|
||||
QtWidgets.QSizePolicy.Policy.Preferred,
|
||||
QtWidgets.QSizePolicy.Policy.Preferred,
|
||||
)
|
||||
sizePolicy.setHorizontalStretch(1)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.widgetFadeVolume.sizePolicy().hasHeightForWidth())
|
||||
sizePolicy.setHeightForWidth(
|
||||
self.widgetFadeVolume.sizePolicy().hasHeightForWidth()
|
||||
)
|
||||
self.widgetFadeVolume.setSizePolicy(sizePolicy)
|
||||
self.widgetFadeVolume.setMinimumSize(QtCore.QSize(0, 0))
|
||||
self.widgetFadeVolume.setObjectName("widgetFadeVolume")
|
||||
@ -383,7 +463,11 @@ class Ui_MainWindow(object):
|
||||
self.btnFade.setMinimumSize(QtCore.QSize(132, 32))
|
||||
self.btnFade.setMaximumSize(QtCore.QSize(164, 16777215))
|
||||
icon4 = QtGui.QIcon()
|
||||
icon4.addPixmap(QtGui.QPixmap(":/icons/fade"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||
icon4.addPixmap(
|
||||
QtGui.QPixmap(":/icons/fade"),
|
||||
QtGui.QIcon.Mode.Normal,
|
||||
QtGui.QIcon.State.Off,
|
||||
)
|
||||
self.btnFade.setIcon(icon4)
|
||||
self.btnFade.setIconSize(QtCore.QSize(30, 30))
|
||||
self.btnFade.setObjectName("btnFade")
|
||||
@ -391,7 +475,11 @@ class Ui_MainWindow(object):
|
||||
self.btnStop = QtWidgets.QPushButton(parent=self.frame)
|
||||
self.btnStop.setMinimumSize(QtCore.QSize(0, 36))
|
||||
icon5 = QtGui.QIcon()
|
||||
icon5.addPixmap(QtGui.QPixmap(":/icons/stopsign"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||
icon5.addPixmap(
|
||||
QtGui.QPixmap(":/icons/stopsign"),
|
||||
QtGui.QIcon.Mode.Normal,
|
||||
QtGui.QIcon.State.Off,
|
||||
)
|
||||
self.btnStop.setIcon(icon5)
|
||||
self.btnStop.setObjectName("btnStop")
|
||||
self.verticalLayout_5.addWidget(self.btnStop)
|
||||
@ -415,39 +503,71 @@ class Ui_MainWindow(object):
|
||||
MainWindow.setStatusBar(self.statusbar)
|
||||
self.actionPlay_next = QtGui.QAction(parent=MainWindow)
|
||||
icon6 = QtGui.QIcon()
|
||||
icon6.addPixmap(QtGui.QPixmap("app/ui/../../../../../../.designer/backup/icon-play.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||
icon6.addPixmap(
|
||||
QtGui.QPixmap("app/ui/../../../../../../.designer/backup/icon-play.png"),
|
||||
QtGui.QIcon.Mode.Normal,
|
||||
QtGui.QIcon.State.Off,
|
||||
)
|
||||
self.actionPlay_next.setIcon(icon6)
|
||||
self.actionPlay_next.setObjectName("actionPlay_next")
|
||||
self.actionSkipToNext = QtGui.QAction(parent=MainWindow)
|
||||
icon7 = QtGui.QIcon()
|
||||
icon7.addPixmap(QtGui.QPixmap(":/icons/next"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||
icon7.addPixmap(
|
||||
QtGui.QPixmap(":/icons/next"),
|
||||
QtGui.QIcon.Mode.Normal,
|
||||
QtGui.QIcon.State.Off,
|
||||
)
|
||||
self.actionSkipToNext.setIcon(icon7)
|
||||
self.actionSkipToNext.setObjectName("actionSkipToNext")
|
||||
self.actionInsertTrack = QtGui.QAction(parent=MainWindow)
|
||||
icon8 = QtGui.QIcon()
|
||||
icon8.addPixmap(QtGui.QPixmap("app/ui/../../../../../../.designer/backup/icon_search_database.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||
icon8.addPixmap(
|
||||
QtGui.QPixmap(
|
||||
"app/ui/../../../../../../.designer/backup/icon_search_database.png"
|
||||
),
|
||||
QtGui.QIcon.Mode.Normal,
|
||||
QtGui.QIcon.State.Off,
|
||||
)
|
||||
self.actionInsertTrack.setIcon(icon8)
|
||||
self.actionInsertTrack.setObjectName("actionInsertTrack")
|
||||
self.actionAdd_file = QtGui.QAction(parent=MainWindow)
|
||||
icon9 = QtGui.QIcon()
|
||||
icon9.addPixmap(QtGui.QPixmap("app/ui/../../../../../../.designer/backup/icon_open_file.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||
icon9.addPixmap(
|
||||
QtGui.QPixmap(
|
||||
"app/ui/../../../../../../.designer/backup/icon_open_file.png"
|
||||
),
|
||||
QtGui.QIcon.Mode.Normal,
|
||||
QtGui.QIcon.State.Off,
|
||||
)
|
||||
self.actionAdd_file.setIcon(icon9)
|
||||
self.actionAdd_file.setObjectName("actionAdd_file")
|
||||
self.actionFade = QtGui.QAction(parent=MainWindow)
|
||||
icon10 = QtGui.QIcon()
|
||||
icon10.addPixmap(QtGui.QPixmap("app/ui/../../../../../../.designer/backup/icon-fade.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||
icon10.addPixmap(
|
||||
QtGui.QPixmap("app/ui/../../../../../../.designer/backup/icon-fade.png"),
|
||||
QtGui.QIcon.Mode.Normal,
|
||||
QtGui.QIcon.State.Off,
|
||||
)
|
||||
self.actionFade.setIcon(icon10)
|
||||
self.actionFade.setObjectName("actionFade")
|
||||
self.actionStop = QtGui.QAction(parent=MainWindow)
|
||||
icon11 = QtGui.QIcon()
|
||||
icon11.addPixmap(QtGui.QPixmap(":/icons/stop"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||
icon11.addPixmap(
|
||||
QtGui.QPixmap(":/icons/stop"),
|
||||
QtGui.QIcon.Mode.Normal,
|
||||
QtGui.QIcon.State.Off,
|
||||
)
|
||||
self.actionStop.setIcon(icon11)
|
||||
self.actionStop.setObjectName("actionStop")
|
||||
self.action_Clear_selection = QtGui.QAction(parent=MainWindow)
|
||||
self.action_Clear_selection.setObjectName("action_Clear_selection")
|
||||
self.action_Resume_previous = QtGui.QAction(parent=MainWindow)
|
||||
icon12 = QtGui.QIcon()
|
||||
icon12.addPixmap(QtGui.QPixmap(":/icons/previous"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
|
||||
icon12.addPixmap(
|
||||
QtGui.QPixmap(":/icons/previous"),
|
||||
QtGui.QIcon.Mode.Normal,
|
||||
QtGui.QIcon.State.Off,
|
||||
)
|
||||
self.action_Resume_previous.setIcon(icon12)
|
||||
self.action_Resume_previous.setObjectName("action_Resume_previous")
|
||||
self.actionE_xit = QtGui.QAction(parent=MainWindow)
|
||||
@ -494,7 +614,9 @@ class Ui_MainWindow(object):
|
||||
self.actionImport = QtGui.QAction(parent=MainWindow)
|
||||
self.actionImport.setObjectName("actionImport")
|
||||
self.actionDownload_CSV_of_played_tracks = QtGui.QAction(parent=MainWindow)
|
||||
self.actionDownload_CSV_of_played_tracks.setObjectName("actionDownload_CSV_of_played_tracks")
|
||||
self.actionDownload_CSV_of_played_tracks.setObjectName(
|
||||
"actionDownload_CSV_of_played_tracks"
|
||||
)
|
||||
self.actionSearch = QtGui.QAction(parent=MainWindow)
|
||||
self.actionSearch.setObjectName("actionSearch")
|
||||
self.actionInsertSectionHeader = QtGui.QAction(parent=MainWindow)
|
||||
@ -522,9 +644,13 @@ class Ui_MainWindow(object):
|
||||
self.actionResume = QtGui.QAction(parent=MainWindow)
|
||||
self.actionResume.setObjectName("actionResume")
|
||||
self.actionSearch_title_in_Wikipedia = QtGui.QAction(parent=MainWindow)
|
||||
self.actionSearch_title_in_Wikipedia.setObjectName("actionSearch_title_in_Wikipedia")
|
||||
self.actionSearch_title_in_Wikipedia.setObjectName(
|
||||
"actionSearch_title_in_Wikipedia"
|
||||
)
|
||||
self.actionSearch_title_in_Songfacts = QtGui.QAction(parent=MainWindow)
|
||||
self.actionSearch_title_in_Songfacts.setObjectName("actionSearch_title_in_Songfacts")
|
||||
self.actionSearch_title_in_Songfacts.setObjectName(
|
||||
"actionSearch_title_in_Songfacts"
|
||||
)
|
||||
self.actionSelect_duplicate_rows = QtGui.QAction(parent=MainWindow)
|
||||
self.actionSelect_duplicate_rows.setObjectName("actionSelect_duplicate_rows")
|
||||
self.actionReplace_files = QtGui.QAction(parent=MainWindow)
|
||||
@ -578,7 +704,7 @@ class Ui_MainWindow(object):
|
||||
self.retranslateUi(MainWindow)
|
||||
self.tabPlaylist.setCurrentIndex(-1)
|
||||
self.tabInfolist.setCurrentIndex(-1)
|
||||
self.actionE_xit.triggered.connect(MainWindow.close) # type: ignore
|
||||
self.actionE_xit.triggered.connect(MainWindow.close) # type: ignore
|
||||
QtCore.QMetaObject.connectSlotsByName(MainWindow)
|
||||
|
||||
def retranslateUi(self, MainWindow):
|
||||
@ -619,38 +745,58 @@ class Ui_MainWindow(object):
|
||||
self.actionFade.setShortcut(_translate("MainWindow", "Ctrl+Z"))
|
||||
self.actionStop.setText(_translate("MainWindow", "S&top"))
|
||||
self.actionStop.setShortcut(_translate("MainWindow", "Ctrl+Alt+S"))
|
||||
self.action_Clear_selection.setText(_translate("MainWindow", "Clear &selection"))
|
||||
self.action_Clear_selection.setText(
|
||||
_translate("MainWindow", "Clear &selection")
|
||||
)
|
||||
self.action_Clear_selection.setShortcut(_translate("MainWindow", "Esc"))
|
||||
self.action_Resume_previous.setText(_translate("MainWindow", "&Resume previous"))
|
||||
self.action_Resume_previous.setText(
|
||||
_translate("MainWindow", "&Resume previous")
|
||||
)
|
||||
self.actionE_xit.setText(_translate("MainWindow", "E&xit"))
|
||||
self.actionTest.setText(_translate("MainWindow", "&Test"))
|
||||
self.actionOpenPlaylist.setText(_translate("MainWindow", "O&pen..."))
|
||||
self.actionNewPlaylist.setText(_translate("MainWindow", "&New..."))
|
||||
self.actionTestFunction.setText(_translate("MainWindow", "&Test function"))
|
||||
self.actionSkipToFade.setText(_translate("MainWindow", "&Skip to start of fade"))
|
||||
self.actionSkipToFade.setText(
|
||||
_translate("MainWindow", "&Skip to start of fade")
|
||||
)
|
||||
self.actionSkipToEnd.setText(_translate("MainWindow", "Skip to &end of track"))
|
||||
self.actionClosePlaylist.setText(_translate("MainWindow", "&Close"))
|
||||
self.actionRenamePlaylist.setText(_translate("MainWindow", "&Rename..."))
|
||||
self.actionDeletePlaylist.setText(_translate("MainWindow", "Dele&te..."))
|
||||
self.actionMoveSelected.setText(_translate("MainWindow", "Mo&ve selected tracks to..."))
|
||||
self.actionMoveSelected.setText(
|
||||
_translate("MainWindow", "Mo&ve selected tracks to...")
|
||||
)
|
||||
self.actionExport_playlist.setText(_translate("MainWindow", "E&xport..."))
|
||||
self.actionSetNext.setText(_translate("MainWindow", "Set &next"))
|
||||
self.actionSetNext.setShortcut(_translate("MainWindow", "Ctrl+N"))
|
||||
self.actionSelect_next_track.setText(_translate("MainWindow", "Select next track"))
|
||||
self.actionSelect_next_track.setText(
|
||||
_translate("MainWindow", "Select next track")
|
||||
)
|
||||
self.actionSelect_next_track.setShortcut(_translate("MainWindow", "J"))
|
||||
self.actionSelect_previous_track.setText(_translate("MainWindow", "Select previous track"))
|
||||
self.actionSelect_previous_track.setText(
|
||||
_translate("MainWindow", "Select previous track")
|
||||
)
|
||||
self.actionSelect_previous_track.setShortcut(_translate("MainWindow", "K"))
|
||||
self.actionSelect_played_tracks.setText(_translate("MainWindow", "Select played tracks"))
|
||||
self.actionMoveUnplayed.setText(_translate("MainWindow", "Move &unplayed tracks to..."))
|
||||
self.actionSelect_played_tracks.setText(
|
||||
_translate("MainWindow", "Select played tracks")
|
||||
)
|
||||
self.actionMoveUnplayed.setText(
|
||||
_translate("MainWindow", "Move &unplayed tracks to...")
|
||||
)
|
||||
self.actionAdd_note.setText(_translate("MainWindow", "Add note..."))
|
||||
self.actionAdd_note.setShortcut(_translate("MainWindow", "Ctrl+T"))
|
||||
self.actionEnable_controls.setText(_translate("MainWindow", "Enable controls"))
|
||||
self.actionImport.setText(_translate("MainWindow", "Import track..."))
|
||||
self.actionImport.setShortcut(_translate("MainWindow", "Ctrl+Shift+I"))
|
||||
self.actionDownload_CSV_of_played_tracks.setText(_translate("MainWindow", "Download CSV of played tracks..."))
|
||||
self.actionDownload_CSV_of_played_tracks.setText(
|
||||
_translate("MainWindow", "Download CSV of played tracks...")
|
||||
)
|
||||
self.actionSearch.setText(_translate("MainWindow", "Search..."))
|
||||
self.actionSearch.setShortcut(_translate("MainWindow", "/"))
|
||||
self.actionInsertSectionHeader.setText(_translate("MainWindow", "Insert §ion header..."))
|
||||
self.actionInsertSectionHeader.setText(
|
||||
_translate("MainWindow", "Insert §ion header...")
|
||||
)
|
||||
self.actionInsertSectionHeader.setShortcut(_translate("MainWindow", "Ctrl+H"))
|
||||
self.actionRemove.setText(_translate("MainWindow", "&Remove track"))
|
||||
self.actionFind_next.setText(_translate("MainWindow", "Find next"))
|
||||
@ -658,8 +804,12 @@ class Ui_MainWindow(object):
|
||||
self.actionFind_previous.setText(_translate("MainWindow", "Find previous"))
|
||||
self.actionFind_previous.setShortcut(_translate("MainWindow", "P"))
|
||||
self.action_About.setText(_translate("MainWindow", "&About"))
|
||||
self.actionSave_as_template.setText(_translate("MainWindow", "Save as template..."))
|
||||
self.actionNew_from_template.setText(_translate("MainWindow", "New from template..."))
|
||||
self.actionSave_as_template.setText(
|
||||
_translate("MainWindow", "Save as template...")
|
||||
)
|
||||
self.actionNew_from_template.setText(
|
||||
_translate("MainWindow", "New from template...")
|
||||
)
|
||||
self.actionDebug.setText(_translate("MainWindow", "Debug"))
|
||||
self.actionAdd_cart.setText(_translate("MainWindow", "Edit cart &1..."))
|
||||
self.actionMark_for_moving.setText(_translate("MainWindow", "Mark for moving"))
|
||||
@ -668,11 +818,23 @@ class Ui_MainWindow(object):
|
||||
self.actionPaste.setShortcut(_translate("MainWindow", "Ctrl+V"))
|
||||
self.actionResume.setText(_translate("MainWindow", "Resume"))
|
||||
self.actionResume.setShortcut(_translate("MainWindow", "Ctrl+R"))
|
||||
self.actionSearch_title_in_Wikipedia.setText(_translate("MainWindow", "Search title in Wikipedia"))
|
||||
self.actionSearch_title_in_Wikipedia.setShortcut(_translate("MainWindow", "Ctrl+W"))
|
||||
self.actionSearch_title_in_Songfacts.setText(_translate("MainWindow", "Search title in Songfacts"))
|
||||
self.actionSearch_title_in_Songfacts.setShortcut(_translate("MainWindow", "Ctrl+S"))
|
||||
self.actionSelect_duplicate_rows.setText(_translate("MainWindow", "Select duplicate rows..."))
|
||||
self.actionSearch_title_in_Wikipedia.setText(
|
||||
_translate("MainWindow", "Search title in Wikipedia")
|
||||
)
|
||||
self.actionSearch_title_in_Wikipedia.setShortcut(
|
||||
_translate("MainWindow", "Ctrl+W")
|
||||
)
|
||||
self.actionSearch_title_in_Songfacts.setText(
|
||||
_translate("MainWindow", "Search title in Songfacts")
|
||||
)
|
||||
self.actionSearch_title_in_Songfacts.setShortcut(
|
||||
_translate("MainWindow", "Ctrl+S")
|
||||
)
|
||||
self.actionSelect_duplicate_rows.setText(
|
||||
_translate("MainWindow", "Select duplicate rows...")
|
||||
)
|
||||
self.actionReplace_files.setText(_translate("MainWindow", "Import files..."))
|
||||
from infotabs import InfoTabs
|
||||
from pyqtgraph import PlotWidget
|
||||
|
||||
|
||||
from infotabs import InfoTabs # type: ignore
|
||||
from pyqtgraph import PlotWidget # type: ignore
|
||||
|
||||
Loading…
Reference in New Issue
Block a user