diff --git a/app/models.py b/app/models.py index cec667d..770407f 100644 --- a/app/models.py +++ b/app/models.py @@ -91,18 +91,18 @@ class NoteColours(dbtables.NoteColoursTable): cache_key = "note_colours_all" cached_result = cache_region.get(cache_key) - if cached_result is NO_VALUE: - # Query the database - result = session.scalars( - select(cls) - .where( - cls.enabled.is_(True), - ) - .order_by(cls.order) - ).all() - cache_region.set(cache_key, result) - else: - result = cached_result + if cached_result is not NO_VALUE: + return cached_result + + # Query the database + result = session.scalars( + select(cls) + .where( + cls.enabled.is_(True), + ) + .order_by(cls.order) + ).all() + cache_region.set(cache_key, result) return result @@ -143,7 +143,8 @@ class NoteColours(dbtables.NoteColoursTable): return rec.colour return "" - def invalidate_cache(self) -> None: + @staticmethod + def invalidate_cache() -> None: """Invalidate dogpile cache""" cache_region.delete("note_colours_all") diff --git a/app/musicmuster.py b/app/musicmuster.py index 2dceb22..d7ce506 100755 --- a/app/musicmuster.py +++ b/app/musicmuster.py @@ -57,7 +57,6 @@ from PyQt6.QtWidgets import ( ) # Third party imports -import line_profiler from pygame import mixer from sqlalchemy.orm.session import Session import stackprinter # type: ignore @@ -694,6 +693,7 @@ class PreviewManager: mixer.init() self.intro: Optional[int] = None self.path: str = "" + self.row_number: Optional[int] = None self.start_time: Optional[dt.datetime] = None self.track_id: int = 0 @@ -2114,7 +2114,6 @@ class Window(QMainWindow): webbrowser.get("browser").open_new_tab(url) - @line_profiler.profile def paste_rows(self, dummy_for_profiling: int | None = None) -> None: """ Paste earlier cut rows. @@ -2255,6 +2254,7 @@ class Window(QMainWindow): return if not track_info: return + self.preview_manager.row_number = track_info.row_number with db.Session() as session: track = session.get(Tracks, track_info.track_id) if not track: @@ -2278,7 +2278,9 @@ class Window(QMainWindow): def preview_arm(self): """Manager arm button for setting intro length""" - self.footer_section.btnPreviewMark.setEnabled(self.btnPreviewArm.isChecked()) + self.footer_section.btnPreviewMark.setEnabled( + self.footer_section.btnPreviewArm.isChecked() + ) def preview_back(self) -> None: """Wind back preview file""" diff --git a/app/playlistmodel.py b/app/playlistmodel.py index 4be929f..d9ba24a 100644 --- a/app/playlistmodel.py +++ b/app/playlistmodel.py @@ -184,7 +184,7 @@ class PlaylistModel(QAbstractTableModel): self.signals.resize_rows_signal.emit(self.playlist_id) - def background_role(self, row: int, column: int, rat: RowAndTrack) -> QBrush: + def _background_role(self, row: int, column: int, rat: RowAndTrack) -> QBrush: """Return background setting""" # Handle entire row colouring @@ -333,7 +333,7 @@ class PlaylistModel(QAbstractTableModel): def data( self, index: QModelIndex, role: int = Qt.ItemDataRole.DisplayRole - ) -> QVariant | QFont | QBrush | str: + ) -> QVariant | QFont | QBrush | str | int: """Return data to view""" if ( @@ -360,17 +360,17 @@ class PlaylistModel(QAbstractTableModel): # These are ordered in approximately the frequency with which # they are called if role == Qt.ItemDataRole.BackgroundRole: - return self.background_role(row, column, rat) + return self._background_role(row, column, rat) elif role == Qt.ItemDataRole.DisplayRole: - return self.display_role(row, column, rat) + return self._display_role(row, column, rat) elif role == Qt.ItemDataRole.EditRole: - return self.edit_role(row, column, rat) + return self._edit_role(row, column, rat) elif role == Qt.ItemDataRole.FontRole: - return self.font_role(row, column, rat) + return self._font_role(row, column, rat) elif role == Qt.ItemDataRole.ForegroundRole: - return self.foreground_role(row, column, rat) + return self._foreground_role(row, column, rat) elif role == Qt.ItemDataRole.ToolTipRole: - return self.tooltip_role(row, column, rat) + return self._tooltip_role(row, column, rat) return QVariant() @@ -403,7 +403,7 @@ class PlaylistModel(QAbstractTableModel): self.reset_track_sequence_row_numbers() self.update_track_times() - def display_role(self, row: int, column: int, rat: RowAndTrack) -> str: + def _display_role(self, row: int, column: int, rat: RowAndTrack) -> str: """ Return text for display """ @@ -478,9 +478,9 @@ class PlaylistModel(QAbstractTableModel): super().endResetModel() self.reset_track_sequence_row_numbers() - def edit_role(self, row: int, column: int, rat: RowAndTrack) -> str: + def _edit_role(self, row: int, column: int, rat: RowAndTrack) -> str | int: """ - Return text for editing + Return value for editing """ # If this is a header row and we're being asked for the @@ -489,7 +489,7 @@ class PlaylistModel(QAbstractTableModel): return rat.note if column == Col.INTRO.value: - return str(rat.intro or "") + return rat.intro or 0 if column == Col.TITLE.value: return rat.title if column == Col.ARTIST.value: @@ -499,7 +499,7 @@ class PlaylistModel(QAbstractTableModel): return "" - def foreground_role(self, row: int, column: int, rat: RowAndTrack) -> QBrush: + def _foreground_role(self, row: int, column: int, rat: RowAndTrack) -> QBrush: """Return header foreground colour or QBrush() if none""" if self.is_header_row(row): @@ -536,7 +536,7 @@ class PlaylistModel(QAbstractTableModel): return default - def font_role(self, row: int, column: int, rat: RowAndTrack) -> QFont: + def _font_role(self, row: int, column: int, rat: RowAndTrack) -> QFont: """ Return font """ @@ -644,22 +644,22 @@ class PlaylistModel(QAbstractTableModel): section: int, orientation: Qt.Orientation, role: int = Qt.ItemDataRole.DisplayRole, - ) -> QVariant: + ) -> str | int | QFont | QVariant: """ Return text for headers """ display_dispatch_table = { - Col.START_GAP.value: QVariant(Config.HEADER_START_GAP), - Col.INTRO.value: QVariant(Config.HEADER_INTRO), - Col.TITLE.value: QVariant(Config.HEADER_TITLE), - Col.ARTIST.value: QVariant(Config.HEADER_ARTIST), - Col.DURATION.value: QVariant(Config.HEADER_DURATION), - Col.START_TIME.value: QVariant(Config.HEADER_START_TIME), - Col.END_TIME.value: QVariant(Config.HEADER_END_TIME), - Col.LAST_PLAYED.value: QVariant(Config.HEADER_LAST_PLAYED), - Col.BITRATE.value: QVariant(Config.HEADER_BITRATE), - Col.NOTE.value: QVariant(Config.HEADER_NOTE), + Col.START_GAP.value: Config.HEADER_START_GAP, + Col.INTRO.value: Config.HEADER_INTRO, + Col.TITLE.value: Config.HEADER_TITLE, + Col.ARTIST.value: Config.HEADER_ARTIST, + Col.DURATION.value: Config.HEADER_DURATION, + Col.START_TIME.value: Config.HEADER_START_TIME, + Col.END_TIME.value: Config.HEADER_END_TIME, + Col.LAST_PLAYED.value: Config.HEADER_LAST_PLAYED, + Col.BITRATE.value: Config.HEADER_BITRATE, + Col.NOTE.value: Config.HEADER_NOTE, } if role == Qt.ItemDataRole.DisplayRole: @@ -667,14 +667,14 @@ class PlaylistModel(QAbstractTableModel): return display_dispatch_table[section] else: if Config.ROWS_FROM_ZERO: - return QVariant(str(section)) + return section else: - return QVariant(str(section + 1)) + return section + 1 elif role == Qt.ItemDataRole.FontRole: boldfont = QFont() boldfont.setBold(True) - return QVariant(boldfont) + return boldfont return QVariant() @@ -1616,7 +1616,7 @@ class PlaylistModel(QAbstractTableModel): def supportedDropActions(self) -> Qt.DropAction: return Qt.DropAction.MoveAction | Qt.DropAction.CopyAction - def tooltip_role(self, row: int, column: int, rat: RowAndTrack) -> str: + def _tooltip_role(self, row: int, column: int, rat: RowAndTrack) -> str: """ Return tooltip. Currently only used for last_played column. """ diff --git a/app/playlists.py b/app/playlists.py index 3dc9413..9daf456 100644 --- a/app/playlists.py +++ b/app/playlists.py @@ -31,7 +31,6 @@ from PyQt6.QtWidgets import ( ) # Third party imports -import line_profiler # App imports from audacity_controller import AudacityController @@ -184,11 +183,11 @@ class PlaylistDelegate(QStyledItemDelegate): data_modified = False if isinstance(editor, QTextEdit): data_modified = ( - self.original_model_data.value() != editor.toPlainText() + self.original_model_data != editor.toPlainText() ) elif isinstance(editor, QDoubleSpinBox): data_modified = ( - self.original_model_data.value() != int(editor.value()) * 1000 + self.original_model_data != int(editor.value()) * 1000 ) if not data_modified: self.closeEditor.emit(editor) @@ -247,10 +246,10 @@ class PlaylistDelegate(QStyledItemDelegate): edit_index, Qt.ItemDataRole.EditRole ) if index.column() == Col.INTRO.value: - if self.original_model_data.value(): - editor.setValue(self.original_model_data.value() / 1000) + if self.original_model_data: + editor.setValue(self.original_model_data / 1000) else: - editor.setPlainText(self.original_model_data.value()) + editor.setPlainText(self.original_model_data) def setModelData(self, editor, model, index): proxy_model = index.model() @@ -358,7 +357,6 @@ class PlaylistTab(QTableView): # Deselect edited line self.clear_selection() - @line_profiler.profile def dropEvent(self, event: Optional[QDropEvent], dummy: int | None = None) -> None: """ Move dropped rows diff --git a/app/querylistmodel.py b/app/querylistmodel.py index 995972b..954074e 100644 --- a/app/querylistmodel.py +++ b/app/querylistmodel.py @@ -15,6 +15,7 @@ from PyQt6.QtCore import ( QVariant, ) from PyQt6.QtGui import ( + QBrush, QColor, QFont, ) @@ -82,27 +83,27 @@ class QuerylistModel(QAbstractTableModel): def __repr__(self) -> str: return f"" - def background_role(self, row: int, column: int, qrow: QueryRow) -> QVariant: + def _background_role(self, row: int, column: int, qrow: QueryRow) -> QBrush: """Return background setting""" # Unreadable track file if file_is_unreadable(qrow.path): - return QVariant(QColor(Config.COLOUR_UNREADABLE)) + return QBrush(QColor(Config.COLOUR_UNREADABLE)) # Selected row if row in self._selected_rows: - return QVariant(QColor(Config.COLOUR_QUERYLIST_SELECTED)) + return QBrush(QColor(Config.COLOUR_QUERYLIST_SELECTED)) # Individual cell colouring if column == QueryCol.BITRATE.value: if not qrow.bitrate or qrow.bitrate < Config.BITRATE_LOW_THRESHOLD: - return QVariant(QColor(Config.COLOUR_BITRATE_LOW)) + return QBrush(QColor(Config.COLOUR_BITRATE_LOW)) elif qrow.bitrate < Config.BITRATE_OK_THRESHOLD: - return QVariant(QColor(Config.COLOUR_BITRATE_MEDIUM)) + return QBrush(QColor(Config.COLOUR_BITRATE_MEDIUM)) else: - return QVariant(QColor(Config.COLOUR_BITRATE_OK)) + return QBrush(QColor(Config.COLOUR_BITRATE_OK)) - return QVariant() + return QBrush() def columnCount(self, parent: QModelIndex = QModelIndex()) -> int: """Standard function for view""" @@ -114,7 +115,23 @@ class QuerylistModel(QAbstractTableModel): ) -> QVariant: """Return data to view""" - if not index.isValid() or not (0 <= index.row() < len(self.querylist_rows)): + if ( + not index.isValid() + or not (0 <= index.row() < len(self.querylist_rows)) + or role + in [ + Qt.ItemDataRole.CheckStateRole, + Qt.ItemDataRole.DecorationRole, + Qt.ItemDataRole.EditRole, + Qt.ItemDataRole.FontRole, + Qt.ItemDataRole.ForegroundRole, + Qt.ItemDataRole.InitialSortOrderRole, + Qt.ItemDataRole.SizeHintRole, + Qt.ItemDataRole.StatusTipRole, + Qt.ItemDataRole.TextAlignmentRole, + Qt.ItemDataRole.WhatsThisRole, + ] + ): return QVariant() row = index.row() @@ -124,48 +141,33 @@ class QuerylistModel(QAbstractTableModel): # Dispatch to role-specific functions dispatch_table: dict[int, Callable] = { - int(Qt.ItemDataRole.BackgroundRole): self.background_role, - int(Qt.ItemDataRole.DisplayRole): self.display_role, - int(Qt.ItemDataRole.ToolTipRole): self.tooltip_role, + int(Qt.ItemDataRole.BackgroundRole): self._background_role, + int(Qt.ItemDataRole.DisplayRole): self._display_role, + int(Qt.ItemDataRole.ToolTipRole): self._tooltip_role, } if role in dispatch_table: return QVariant(dispatch_table[role](row, column, qrow)) - # Document other roles but don't use them - if role in [ - Qt.ItemDataRole.DecorationRole, - Qt.ItemDataRole.EditRole, - Qt.ItemDataRole.FontRole, - Qt.ItemDataRole.ForegroundRole, - Qt.ItemDataRole.InitialSortOrderRole, - Qt.ItemDataRole.SizeHintRole, - Qt.ItemDataRole.StatusTipRole, - Qt.ItemDataRole.TextAlignmentRole, - Qt.ItemDataRole.ToolTipRole, - Qt.ItemDataRole.WhatsThisRole, - ]: - return QVariant() - # Fall through to no-op return QVariant() - def display_role(self, row: int, column: int, qrow: QueryRow) -> QVariant: + def _display_role(self, row: int, column: int, qrow: QueryRow) -> str: """ Return text for display """ dispatch_table = { - QueryCol.ARTIST.value: QVariant(qrow.artist), - QueryCol.BITRATE.value: QVariant(qrow.bitrate), - QueryCol.DURATION.value: QVariant(ms_to_mmss(qrow.duration)), - QueryCol.LAST_PLAYED.value: QVariant(get_relative_date(qrow.lastplayed)), - QueryCol.TITLE.value: QVariant(qrow.title), + QueryCol.ARTIST.value: qrow.artist, + QueryCol.BITRATE.value: str(qrow.bitrate), + QueryCol.DURATION.value: ms_to_mmss(qrow.duration), + QueryCol.LAST_PLAYED.value: get_relative_date(qrow.lastplayed), + QueryCol.TITLE.value: qrow.title, } if column in dispatch_table: return dispatch_table[column] - return QVariant() + return "" def flags(self, index: QModelIndex) -> Qt.ItemFlag: """ @@ -266,7 +268,7 @@ class QuerylistModel(QAbstractTableModel): bottom_right = self.index(row, self.columnCount() - 1) self.dataChanged.emit(top_left, bottom_right, [Qt.ItemDataRole.BackgroundRole]) - def tooltip_role(self, row: int, column: int, rat: RowAndTrack) -> QVariant: + def _tooltip_role(self, row: int, column: int, rat: RowAndTrack) -> str | QVariant: """ Return tooltip. Currently only used for last_played column. """ @@ -278,7 +280,7 @@ class QuerylistModel(QAbstractTableModel): if not track_id: return QVariant() playdates = Playdates.last_playdates(session, track_id) - return QVariant( + return ( "
".join( [ a.lastplayed.strftime(Config.LAST_PLAYED_TOOLTIP_DATE_FORMAT)