194 lines
6.0 KiB
Python
194 lines
6.0 KiB
Python
# 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
|