# Standard library imports from typing import cast, Optional, TYPE_CHECKING # PyQt imports from PyQt6.QtCore import ( QTimer, ) from PyQt6.QtWidgets import ( QAbstractItemView, QTableView, ) # Third party imports # import line_profiler # App imports from audacity_controller import AudacityController from classes import ApplicationError, MusicMusterSignals, PlaylistStyle from config import Config from helpers import ( show_warning, ) from log import log from models import db, Settings from querylistmodel import QuerylistModel if TYPE_CHECKING: from musicmuster import Window class QuerylistTab(QTableView): """ The querylist view """ def __init__(self, musicmuster: "Window", model: QuerylistModel) -> None: super().__init__() # Save passed settings self.musicmuster = musicmuster self.playlist_id = model.playlist_id # Set up widget self.setAlternatingRowColors(True) self.setVerticalScrollMode(QAbstractItemView.ScrollMode.ScrollPerPixel) # Set our custom style - this draws the drop indicator across the whole row self.setStyle(PlaylistStyle()) # We will enable dragging when rows are selected. Disabling it # here means we can click and drag to select rows. self.setDragEnabled(False) # Connect signals self.signals = MusicMusterSignals() self.signals.resize_rows_signal.connect(self.resize_rows) # Selection model self.setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection) self.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows) # Enable item editing for checkboxes self.clicked.connect(self.handle_row_click) # Set up for Audacity try: self.ac: Optional[AudacityController] = AudacityController() except ApplicationError as e: 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() if h_header: h_header.sectionResized.connect(self._column_resize) h_header.setStretchLastSection(True) # Resize on vertical header click v_header = self.verticalHeader() if v_header: v_header.setMinimumSectionSize(5) v_header.sectionHandleDoubleClicked.disconnect() v_header.sectionHandleDoubleClicked.connect(self.resizeRowToContents) # Setting ResizeToContents causes screen flash on load self.resize_rows() # ########## Overridden class functions ########## def resizeRowToContents(self, row): super().resizeRowToContents(row) self.verticalHeader().resizeSection(row, self.sizeHintForRow(row)) def resizeRowsToContents(self): header = self.verticalHeader() for row in range(self.model().rowCount()): hint = self.sizeHintForRow(row) header.resizeSection(row, hint) # ########## Custom functions ########## def clear_selection(self) -> None: """Unselect all tracks and reset drag mode""" self.clearSelection() # We want to remove the focus from any widget otherwise keyboard # activity may edit a cell. fw = self.musicmuster.focusWidget() if fw: fw.clearFocus() self.setDragEnabled(False) def _column_resize(self, column_number: int, _old: int, _new: int) -> None: """ Called when column width changes. Save new width to database. """ log.debug(f"_column_resize({column_number=}, {_old=}, {_new=}") header = self.horizontalHeader() if not header: return # Resize rows if necessary self.resizeRowsToContents() with db.Session() as session: attr_name = f"querylist_col_{column_number}_width" record = Settings.get_setting(session, attr_name) record.f_int = self.columnWidth(column_number) session.commit() def handle_row_click(self, index): self.model().toggle_row_selection(index.row()) self.clearSelection() def model(self) -> QuerylistModel: """ Override return type to keep mypy happy in this module """ return cast(QuerylistModel, super().model()) def resize_rows(self, playlist_id: Optional[int] = None) -> None: """ If playlist_id is us, resize rows """ log.debug(f"resize_rows({playlist_id=}) {self.playlist_id=}") if playlist_id and playlist_id != self.playlist_id: return # Suggestion from phind.com def resize_row(row, count=1): row_count = self.model().rowCount() for todo in range(count): if row < row_count: self.resizeRowToContents(row) row += 1 if row < row_count: QTimer.singleShot(0, lambda: resize_row(row, count)) # Start resizing from row 0, 10 rows at a time QTimer.singleShot(0, lambda: resize_row(0, Config.RESIZE_ROW_CHUNK_SIZE)) def _set_column_widths(self) -> None: """Column widths from settings""" log.debug("_set_column_widths()") header = self.horizontalHeader() if not header: return # Last column is set to stretch so ignore it here with db.Session() as session: for column_number in range(header.count() - 1): attr_name = f"querylist_col_{column_number}_width" record = Settings.get_setting(session, attr_name) if record.f_int is not None: self.setColumnWidth(column_number, record.f_int) else: self.setColumnWidth(column_number, Config.DEFAULT_COLUMN_WIDTH) def tab_live(self) -> None: """Noop for query tabs""" return