WIP V3: implement searching with QSortFilterProxyModel (ooo!)
This commit is contained in:
parent
6f5c371510
commit
480c832852
@ -6,7 +6,6 @@ from typing import (
|
||||
cast,
|
||||
List,
|
||||
Optional,
|
||||
Sequence,
|
||||
)
|
||||
from os.path import basename
|
||||
|
||||
@ -49,7 +48,6 @@ from PyQt6.QtWidgets import (
|
||||
QProgressBar,
|
||||
QPushButton,
|
||||
)
|
||||
from sqlalchemy import text
|
||||
import stackprinter # type: ignore
|
||||
|
||||
from classes import (
|
||||
@ -511,12 +509,6 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
self.actionEnable_controls.triggered.connect(self.enable_play_next_controls)
|
||||
self.actionExport_playlist.triggered.connect(self.export_playlist_tab)
|
||||
self.actionFade.triggered.connect(self.fade)
|
||||
self.actionFind_next.triggered.connect(
|
||||
lambda: self.tabPlaylist.currentWidget().search_next()
|
||||
)
|
||||
self.actionFind_previous.triggered.connect(
|
||||
lambda: self.tabPlaylist.currentWidget().search_previous()
|
||||
)
|
||||
self.actionImport.triggered.connect(self.import_track)
|
||||
self.actionInsertSectionHeader.triggered.connect(self.insert_header)
|
||||
self.actionInsertTrack.triggered.connect(self.insert_track)
|
||||
@ -555,7 +547,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
self.hdrNextTrack.clicked.connect(self.show_next)
|
||||
self.tabPlaylist.tabCloseRequested.connect(self.close_tab)
|
||||
self.tabBar = self.tabPlaylist.tabBar()
|
||||
self.txtSearch.returnPressed.connect(self.search_playlist_return)
|
||||
self.txtSearch.textChanged.connect(self.search_playlist_text_changed)
|
||||
|
||||
self.signals.enable_escape_signal.connect(self.enable_escape)
|
||||
self.signals.next_track_changed_signal.connect(self.update_headers)
|
||||
@ -870,7 +862,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
ok = dlg.exec()
|
||||
if ok:
|
||||
model.insert_row(
|
||||
proposed_row_number=self.active_tab().get_selected_row_number(),
|
||||
proposed_row_number=self.active_tab().selected_model_row_number(),
|
||||
note=dlg.textValue(),
|
||||
)
|
||||
|
||||
@ -1249,6 +1241,13 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
self.active_tab().set_search(self.txtSearch.text())
|
||||
self.enable_play_next_controls()
|
||||
|
||||
def search_playlist_text_changed(self) -> None:
|
||||
"""
|
||||
Incremental search of playlist
|
||||
"""
|
||||
|
||||
self.active_model().set_incremental_search(self.txtSearch.text())
|
||||
|
||||
def select_next_row(self) -> None:
|
||||
"""Select next or first row in playlist"""
|
||||
|
||||
|
||||
@ -3,11 +3,12 @@ from datetime import datetime, timedelta
|
||||
from enum import auto, Enum
|
||||
from operator import attrgetter
|
||||
from pprint import pprint
|
||||
from typing import List, Optional
|
||||
from typing import cast, List, Optional
|
||||
|
||||
from PyQt6.QtCore import (
|
||||
QAbstractTableModel,
|
||||
QModelIndex,
|
||||
QRegularExpression,
|
||||
QSortFilterProxyModel,
|
||||
Qt,
|
||||
QVariant,
|
||||
@ -24,10 +25,8 @@ from dbconfig import scoped_session, Session
|
||||
from helpers import (
|
||||
file_is_unreadable,
|
||||
get_embedded_time,
|
||||
get_file_metadata,
|
||||
get_relative_date,
|
||||
open_in_audacity,
|
||||
normalise_track,
|
||||
ms_to_mmss,
|
||||
set_track_metadata,
|
||||
)
|
||||
@ -95,23 +94,6 @@ class StartEndTimes:
|
||||
end_time: Optional[datetime] = None
|
||||
|
||||
|
||||
class PlaylistProxyModel(QSortFilterProxyModel):
|
||||
"""
|
||||
For searching and filtering
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
playlist_id: int,
|
||||
*args,
|
||||
**kwargs,
|
||||
):
|
||||
self.playlist_id = playlist_id
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.setSourceModel(PlaylistModel(playlist_id))
|
||||
|
||||
|
||||
class PlaylistModel(QAbstractTableModel):
|
||||
"""
|
||||
The Playlist Model
|
||||
@ -1002,7 +984,9 @@ class PlaylistModel(QAbstractTableModel):
|
||||
if now_plr:
|
||||
track_sequence.now.plr_rownum = now_plr.plr_rownum
|
||||
if track_sequence.previous.plr_rownum:
|
||||
previous_plr = session.get(PlaylistRows, track_sequence.previous.plr_rownum)
|
||||
previous_plr = session.get(
|
||||
PlaylistRows, track_sequence.previous.plr_rownum
|
||||
)
|
||||
if previous_plr:
|
||||
track_sequence.previous.plr_rownum = previous_plr.plr_rownum
|
||||
|
||||
@ -1243,3 +1227,126 @@ class PlaylistModel(QAbstractTableModel):
|
||||
self.index(updated_row, Col.START_TIME.value),
|
||||
self.index(updated_row, Col.END_TIME.value),
|
||||
)
|
||||
|
||||
|
||||
class PlaylistProxyModel(QSortFilterProxyModel):
|
||||
"""
|
||||
For searching and filtering
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
playlist_model: PlaylistModel,
|
||||
*args,
|
||||
**kwargs,
|
||||
):
|
||||
self.playlist_model = playlist_model
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.setSourceModel(playlist_model)
|
||||
# Search all columns
|
||||
self.setFilterKeyColumn(-1)
|
||||
|
||||
def set_incremental_search(self, search_string: str) -> None:
|
||||
"""
|
||||
Update search pattern
|
||||
"""
|
||||
|
||||
self.setFilterRegularExpression(
|
||||
QRegularExpression(
|
||||
search_string, QRegularExpression.PatternOption.CaseInsensitiveOption
|
||||
)
|
||||
)
|
||||
|
||||
# ######################################
|
||||
# Forward functions not handled in proxy
|
||||
# ######################################
|
||||
|
||||
def delete_rows(self, row_numbers: List[int]) -> None:
|
||||
model = cast(PlaylistModel, self.sourceModel())
|
||||
return model.delete_rows(row_numbers)
|
||||
|
||||
def get_duplicate_rows(self) -> List[int]:
|
||||
model = cast(PlaylistModel, self.sourceModel())
|
||||
return model.get_duplicate_rows()
|
||||
|
||||
def get_rows_duration(self, row_numbers: List[int]) -> int:
|
||||
model = cast(PlaylistModel, self.sourceModel())
|
||||
return model.get_rows_duration(row_numbers)
|
||||
|
||||
def get_row_info(self, row_number: int) -> PlaylistRowData:
|
||||
model = cast(PlaylistModel, self.sourceModel())
|
||||
return model.get_row_info(row_number)
|
||||
|
||||
def get_row_track_path(self, row_number: int) -> str:
|
||||
model = cast(PlaylistModel, self.sourceModel())
|
||||
return model.get_row_track_path(row_number)
|
||||
|
||||
def insert_row(
|
||||
self,
|
||||
proposed_row_number: Optional[int],
|
||||
track_id: Optional[int] = None,
|
||||
note: Optional[str] = None,
|
||||
) -> None:
|
||||
model = cast(PlaylistModel, self.sourceModel())
|
||||
return model.insert_row(proposed_row_number, track_id, note)
|
||||
|
||||
def is_header_row(self, row_number: int) -> bool:
|
||||
model = cast(PlaylistModel, self.sourceModel())
|
||||
return model.is_header_row(row_number)
|
||||
|
||||
def is_unplayed_row(self, row_number: int) -> bool:
|
||||
model = cast(PlaylistModel, self.sourceModel())
|
||||
return model.is_unplayed_row(row_number)
|
||||
|
||||
def mark_unplayed(self, row_numbers: List[int]) -> None:
|
||||
model = cast(PlaylistModel, self.sourceModel())
|
||||
return model.mark_unplayed(row_numbers)
|
||||
|
||||
def move_rows(self, from_rows: List[int], to_row_number: int) -> None:
|
||||
model = cast(PlaylistModel, self.sourceModel())
|
||||
return 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:
|
||||
model = cast(PlaylistModel, self.sourceModel())
|
||||
return model.move_rows_between_playlists(
|
||||
from_rows, to_row_number, to_playlist_id
|
||||
)
|
||||
|
||||
def open_in_audacity(self, row_number: int) -> None:
|
||||
model = cast(PlaylistModel, self.sourceModel())
|
||||
return model.open_in_audacity(row_number)
|
||||
|
||||
def remove_track(self, row_number: int) -> None:
|
||||
model = cast(PlaylistModel, self.sourceModel())
|
||||
return model.remove_track(row_number)
|
||||
|
||||
def rescan_track(self, row_number: int) -> None:
|
||||
model = cast(PlaylistModel, self.sourceModel())
|
||||
return model.rescan_track(row_number)
|
||||
|
||||
def set_next_row(self, row_number: Optional[int]) -> None:
|
||||
model = cast(PlaylistModel, self.sourceModel())
|
||||
return model.set_next_row(row_number)
|
||||
|
||||
def sort_by_artist(self, row_numbers: List[int]) -> None:
|
||||
model = cast(PlaylistModel, self.sourceModel())
|
||||
return model.sort_by_artist(row_numbers)
|
||||
|
||||
def sort_by_duration(self, row_numbers: List[int]) -> None:
|
||||
model = cast(PlaylistModel, self.sourceModel())
|
||||
return model.sort_by_duration(row_numbers)
|
||||
|
||||
def sort_by_lastplayed(self, row_numbers: List[int]) -> None:
|
||||
model = cast(PlaylistModel, self.sourceModel())
|
||||
return model.sort_by_lastplayed(row_numbers)
|
||||
|
||||
def sort_by_title(self, row_numbers: List[int]) -> None:
|
||||
model = cast(PlaylistModel, self.sourceModel())
|
||||
return model.sort_by_title(row_numbers)
|
||||
|
||||
def update_track_times(self) -> None:
|
||||
model = cast(PlaylistModel, self.sourceModel())
|
||||
return model.update_track_times()
|
||||
|
||||
205
app/playlists.py
205
app/playlists.py
@ -1,24 +1,21 @@
|
||||
import os
|
||||
import re
|
||||
import stackprinter # type: ignore
|
||||
import subprocess
|
||||
import threading
|
||||
|
||||
import obsws_python as obs # type: ignore
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
from pprint import pprint
|
||||
from typing import Any, Callable, cast, List, Optional, Tuple, TYPE_CHECKING
|
||||
from typing import Callable, cast, List, Optional, TYPE_CHECKING
|
||||
|
||||
from PyQt6.QtCore import (
|
||||
QEvent,
|
||||
QModelIndex,
|
||||
QObject,
|
||||
QItemSelection,
|
||||
QItemSelectionModel,
|
||||
Qt,
|
||||
# QTimer,
|
||||
)
|
||||
from PyQt6.QtGui import QAction, QBrush, QColor, QFont, QDropEvent, QKeyEvent
|
||||
from PyQt6.QtGui import QAction, QDropEvent, QKeyEvent
|
||||
from PyQt6.QtWidgets import (
|
||||
QAbstractItemDelegate,
|
||||
QAbstractItemView,
|
||||
@ -49,6 +46,7 @@ from helpers import (
|
||||
open_in_audacity,
|
||||
send_mail,
|
||||
set_track_metadata,
|
||||
show_warning,
|
||||
)
|
||||
from log import log
|
||||
from models import PlaylistRows, Settings, Tracks, NoteColours
|
||||
@ -67,8 +65,9 @@ class EscapeDelegate(QStyledItemDelegate):
|
||||
- checks with user before abandoning edit on Escape
|
||||
"""
|
||||
|
||||
def __init__(self, parent) -> None:
|
||||
def __init__(self, parent, playlist_model: PlaylistModel) -> None:
|
||||
super().__init__(parent)
|
||||
self.playlist_model = playlist_model
|
||||
self.signals = MusicMusterSignals()
|
||||
|
||||
def createEditor(
|
||||
@ -123,12 +122,24 @@ class EscapeDelegate(QStyledItemDelegate):
|
||||
return False
|
||||
|
||||
def setEditorData(self, editor, index):
|
||||
value = index.model().data(index, Qt.ItemDataRole.EditRole)
|
||||
model = index.model()
|
||||
if hasattr(model, "mapToSource"):
|
||||
edit_index = model.mapToSource(index)
|
||||
else:
|
||||
edit_index = index
|
||||
|
||||
value = self.playlist_model.data(edit_index, Qt.ItemDataRole.EditRole)
|
||||
editor.setPlainText(value.value())
|
||||
|
||||
def setModelData(self, editor, model, index):
|
||||
model = index.model()
|
||||
if hasattr(model, "mapToSource"):
|
||||
edit_index = model.mapToSource(index)
|
||||
else:
|
||||
edit_index = index
|
||||
|
||||
value = editor.toPlainText()
|
||||
model.setData(index, value, Qt.ItemDataRole.EditRole)
|
||||
self.playlist_model.setData(edit_index, value, Qt.ItemDataRole.EditRole)
|
||||
|
||||
def updateEditorGeometry(self, editor, option, index):
|
||||
editor.setGeometry(option.rect)
|
||||
@ -165,10 +176,10 @@ class PlaylistTab(QTableView):
|
||||
self.playlist_id = playlist_id
|
||||
|
||||
# Set up widget
|
||||
self.setItemDelegate(EscapeDelegate(self))
|
||||
self.playlist_model = PlaylistModel(playlist_id)
|
||||
self.proxy_model = PlaylistProxyModel(self.playlist_model)
|
||||
self.setItemDelegate(EscapeDelegate(self, self.playlist_model))
|
||||
self.setAlternatingRowColors(True)
|
||||
self.setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection)
|
||||
self.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
|
||||
# self.setEditTriggers(QAbstractItemView.EditTrigger.DoubleClicked)
|
||||
self.setVerticalScrollMode(QAbstractItemView.ScrollMode.ScrollPerPixel)
|
||||
self.setDragDropMode(QAbstractItemView.DragDropMode.InternalMove)
|
||||
@ -202,8 +213,12 @@ class PlaylistTab(QTableView):
|
||||
self.sort_undo: List[int] = []
|
||||
# self.edit_cell_type: Optional[int]
|
||||
|
||||
# Selection model
|
||||
self.setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection)
|
||||
self.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
|
||||
|
||||
# Load playlist rows
|
||||
self.setModel(PlaylistProxyModel(playlist_id))
|
||||
self.setModel(self.proxy_model)
|
||||
self._set_column_widths()
|
||||
|
||||
def closeEditor(
|
||||
@ -224,8 +239,7 @@ class PlaylistTab(QTableView):
|
||||
|
||||
# Update start times in case a start time in a note has been
|
||||
# edited
|
||||
model = cast(PlaylistModel, self.model())
|
||||
model.update_track_times()
|
||||
self.playlist_model.update_track_times()
|
||||
|
||||
def dropEvent(self, event):
|
||||
if event.source() is not self or (
|
||||
@ -234,7 +248,7 @@ class PlaylistTab(QTableView):
|
||||
):
|
||||
super().dropEvent(event)
|
||||
|
||||
from_rows = list(set([a.row() for a in self.selectedIndexes()]))
|
||||
from_rows = self.selected_model_row_numbers()
|
||||
to_row = self.indexAt(event.position().toPoint()).row()
|
||||
if (
|
||||
0 <= min(from_rows) <= self.model().rowCount()
|
||||
@ -311,17 +325,76 @@ class PlaylistTab(QTableView):
|
||||
self.clearSelection()
|
||||
self.setDragEnabled(False)
|
||||
|
||||
def get_selected_row_number(self) -> Optional[int]:
|
||||
def selected_display_row_number(self):
|
||||
"""
|
||||
Return the selected row number or None if none selected.
|
||||
"""
|
||||
|
||||
row_index = self._selected_row_index()
|
||||
if row_index:
|
||||
return row_index.row()
|
||||
else:
|
||||
return None
|
||||
return row_index.row()
|
||||
|
||||
def selected_display_row_numbers(self):
|
||||
"""
|
||||
Return a list of the selected row numbers
|
||||
"""
|
||||
|
||||
indexes = self._selected_row_indexes()
|
||||
|
||||
return [a.row() for a in indexes]
|
||||
|
||||
def selected_model_row_number(self) -> Optional[int]:
|
||||
"""
|
||||
Return the model row number corresponding to the selected row or None
|
||||
"""
|
||||
|
||||
selected_index = self._selected_row_index()
|
||||
if selected_index is None:
|
||||
return None
|
||||
if hasattr(self.proxy_model, "mapToSource"):
|
||||
return self.proxy_model.mapToSource(selected_index).row()
|
||||
return selected_index.row()
|
||||
|
||||
def selected_model_row_numbers(self) -> Optional[List[int]]:
|
||||
"""
|
||||
Return a list of model row numbers corresponding to the selected rows or
|
||||
an empty list.
|
||||
"""
|
||||
|
||||
selected_indexes = self._selected_row_indexes()
|
||||
if selected_indexes is None:
|
||||
return None
|
||||
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]
|
||||
|
||||
def _selected_row_index(self) -> Optional[QModelIndex]:
|
||||
"""
|
||||
Return the selected row index or None if none selected.
|
||||
"""
|
||||
|
||||
row_indexes = self._selected_row_indexes()
|
||||
|
||||
if len(row_indexes) != 1:
|
||||
show_warning(
|
||||
self.musicmuster, "No or multiple rows selected", "Select only one row"
|
||||
)
|
||||
return None
|
||||
|
||||
return row_indexes[0]
|
||||
|
||||
def _selected_row_indexes(self) -> List[QModelIndex]:
|
||||
"""
|
||||
Return a list of indexes of column 1 of selected rows
|
||||
"""
|
||||
|
||||
sm = self.selectionModel()
|
||||
if sm and sm.hasSelection():
|
||||
index = sm.currentIndex()
|
||||
if index.isValid():
|
||||
return index.row()
|
||||
return None
|
||||
return sm.selectedRows()
|
||||
return []
|
||||
|
||||
def get_selected_row_track_path(self) -> str:
|
||||
"""
|
||||
@ -329,13 +402,10 @@ class PlaylistTab(QTableView):
|
||||
row does not have a track, return empty string.
|
||||
"""
|
||||
|
||||
sm = self.selectionModel()
|
||||
if sm and sm.hasSelection():
|
||||
index = sm.currentIndex()
|
||||
if index.isValid():
|
||||
model = cast(PlaylistModel, self.model())
|
||||
return model.get_row_track_path(index.row())
|
||||
return ""
|
||||
model_row_number = self.selected_model_row_number()
|
||||
if model_row_number is None:
|
||||
return ""
|
||||
return self.playlist_model.get_row_track_path(model_row_number)
|
||||
|
||||
# def lookup_row_in_songfacts(self) -> None:
|
||||
# """
|
||||
@ -473,11 +543,10 @@ class PlaylistTab(QTableView):
|
||||
Set selected row as next track
|
||||
"""
|
||||
|
||||
selected_row = self.get_selected_row_number()
|
||||
if selected_row is None:
|
||||
model_row_number = self.selected_model_row_number()
|
||||
if model_row_number is None:
|
||||
return
|
||||
model = cast(PlaylistModel, self.model())
|
||||
model.set_next_row(selected_row)
|
||||
self.playlist_model.set_next_row(model_row_number)
|
||||
self.clearSelection()
|
||||
|
||||
# # # ########## Internally called functions ##########
|
||||
@ -485,14 +554,14 @@ class PlaylistTab(QTableView):
|
||||
def _add_track(self) -> None:
|
||||
"""Add a track to a section header making it a normal track row"""
|
||||
|
||||
row_number = self.get_selected_row_number()
|
||||
if not row_number:
|
||||
model_row_number = self.selected_model_row_number()
|
||||
if model_row_number is None:
|
||||
return
|
||||
|
||||
with Session() as session:
|
||||
dlg = TrackSelectDialog(
|
||||
session=session,
|
||||
new_row_number=row_number,
|
||||
new_row_number=model_row_number,
|
||||
playlist_id=self.playlist_id,
|
||||
add_to_header=True,
|
||||
)
|
||||
@ -502,25 +571,31 @@ class PlaylistTab(QTableView):
|
||||
"""Used to process context (right-click) menu, which is defined here"""
|
||||
|
||||
self.menu.clear()
|
||||
model = cast(PlaylistModel, self.model())
|
||||
if not model:
|
||||
return
|
||||
model = self.proxy_model
|
||||
|
||||
row_number = item.row()
|
||||
header_row = model.is_header_row(row_number)
|
||||
display_row_number = item.row()
|
||||
if hasattr(model, "mapToSource"):
|
||||
index = model.index(item.row(), item.column())
|
||||
model_row_number = model.mapToSource(index).row()
|
||||
else:
|
||||
model_row_number = display_row_number
|
||||
|
||||
header_row = model.is_header_row(model_row_number)
|
||||
track_row = not header_row
|
||||
current_row = row_number == track_sequence.now.plr_rownum
|
||||
next_row = row_number == track_sequence.next.plr_rownum
|
||||
current_row = model_row_number == track_sequence.now.plr_rownum
|
||||
next_row = model_row_number == track_sequence.next.plr_rownum
|
||||
|
||||
# Open in Audacity
|
||||
if track_row and not current_row:
|
||||
self._add_context_menu(
|
||||
"Open in Audacity", lambda: model.open_in_audacity(row_number)
|
||||
"Open in Audacity", lambda: model.open_in_audacity(model_row_number)
|
||||
)
|
||||
|
||||
# Rescan
|
||||
if track_row and not current_row:
|
||||
self._add_context_menu("Rescan track", lambda: self._rescan(row_number))
|
||||
self._add_context_menu(
|
||||
"Rescan track", lambda: self._rescan(model_row_number)
|
||||
)
|
||||
|
||||
# ----------------------
|
||||
self.menu.addSeparator()
|
||||
@ -532,7 +607,7 @@ class PlaylistTab(QTableView):
|
||||
# Remove track from row
|
||||
if track_row and not current_row and not next_row:
|
||||
self._add_context_menu(
|
||||
"Remove track from row", lambda: model.remove_track(row_number)
|
||||
"Remove track from row", lambda: model.remove_track(model_row_number)
|
||||
)
|
||||
|
||||
# Add track to section header (ie, make this a track row)
|
||||
@ -543,7 +618,7 @@ class PlaylistTab(QTableView):
|
||||
self.menu.addSeparator()
|
||||
|
||||
# Mark unplayed
|
||||
if track_row and model.is_unplayed_row(row_number):
|
||||
if track_row and model.is_unplayed_row(model_row_number):
|
||||
self._add_context_menu(
|
||||
"Mark unplayed",
|
||||
lambda: self._mark_as_unplayed(self.get_selected_rows()),
|
||||
@ -583,12 +658,12 @@ class PlaylistTab(QTableView):
|
||||
|
||||
# Info
|
||||
if track_row:
|
||||
self._add_context_menu("Info", lambda: self._info_row(row_number))
|
||||
self._add_context_menu("Info", lambda: self._info_row(model_row_number))
|
||||
|
||||
# Track path TODO
|
||||
if track_row:
|
||||
self._add_context_menu(
|
||||
"Copy track path", lambda: self._copy_path(row_number)
|
||||
"Copy track path", lambda: self._copy_path(model_row_number)
|
||||
)
|
||||
|
||||
def _calculate_end_time(
|
||||
@ -631,8 +706,7 @@ class PlaylistTab(QTableView):
|
||||
to the clipboard. Otherwise, return None.
|
||||
"""
|
||||
|
||||
model = cast(PlaylistModel, self.model())
|
||||
track_path = model.get_row_info(row_number).path
|
||||
track_path = self.playlist_model.get_row_info(row_number).path
|
||||
if not track_path:
|
||||
return
|
||||
|
||||
@ -669,8 +743,7 @@ class PlaylistTab(QTableView):
|
||||
if not ask_yes_no("Delete rows", f"Really delete {row_count} row{plural}?"):
|
||||
return
|
||||
|
||||
model = cast(PlaylistModel, self.model())
|
||||
model.delete_rows(self.get_selected_rows())
|
||||
self.playlist_model.delete_rows(self.selected_model_row_numbers())
|
||||
|
||||
def get_selected_rows(self) -> List[int]:
|
||||
"""Return a list of selected row numbers sorted by row"""
|
||||
@ -682,8 +755,7 @@ class PlaylistTab(QTableView):
|
||||
def _info_row(self, row_number: int) -> None:
|
||||
"""Display popup with info re row"""
|
||||
|
||||
model = cast(PlaylistModel, self.model())
|
||||
prd = model.get_row_info(row_number)
|
||||
prd = self.playlist_model.get_row_info(row_number)
|
||||
if prd:
|
||||
txt = (
|
||||
f"Title: {prd.title}\n"
|
||||
@ -739,8 +811,7 @@ class PlaylistTab(QTableView):
|
||||
def _mark_as_unplayed(self, row_numbers: List[int]) -> None:
|
||||
"""Rescan track"""
|
||||
|
||||
model = cast(PlaylistModel, self.model())
|
||||
model.mark_unplayed(row_numbers)
|
||||
self.playlist_model.mark_unplayed(row_numbers)
|
||||
self.clear_selection()
|
||||
|
||||
def _obs_change_scene(self, current_row: int) -> None:
|
||||
@ -786,8 +857,7 @@ class PlaylistTab(QTableView):
|
||||
def _rescan(self, row_number: int) -> None:
|
||||
"""Rescan track"""
|
||||
|
||||
model = cast(PlaylistModel, self.model())
|
||||
model.rescan_track(row_number)
|
||||
self.playlist_model.rescan_track(row_number)
|
||||
self.clear_selection()
|
||||
|
||||
# def _reset_next(self, old_plrid: int, new_plrid: int) -> None:
|
||||
@ -931,8 +1001,7 @@ class PlaylistTab(QTableView):
|
||||
# We need to be in MultiSelection mode
|
||||
self.setSelectionMode(QAbstractItemView.SelectionMode.MultiSelection)
|
||||
# Get the duplicate rows
|
||||
model = cast(PlaylistModel, self.model())
|
||||
duplicate_rows = model.get_duplicate_rows()
|
||||
duplicate_rows = self.playlist_model.get_duplicate_rows()
|
||||
# Select the rows
|
||||
for duplicate_row in duplicate_rows:
|
||||
self.selectRow(duplicate_row)
|
||||
@ -951,8 +1020,9 @@ class PlaylistTab(QTableView):
|
||||
if len(selected_rows) == 0:
|
||||
self.musicmuster.lblSumPlaytime.setText("")
|
||||
else:
|
||||
model = cast(PlaylistModel, self.model())
|
||||
selected_duration = model.get_rows_duration(self.get_selected_rows())
|
||||
selected_duration = self.playlist_model.get_rows_duration(
|
||||
self.get_selected_rows()
|
||||
)
|
||||
if selected_duration > 0:
|
||||
self.musicmuster.lblSumPlaytime.setText(
|
||||
f"Selected duration: {ms_to_mmss(selected_duration)}"
|
||||
@ -1011,6 +1081,14 @@ class PlaylistTab(QTableView):
|
||||
Implement spanning of cells, initiated by signal
|
||||
"""
|
||||
|
||||
model = self.proxy_model
|
||||
if hasattr(model, "mapToSource"):
|
||||
edit_index = model.mapFromSource(
|
||||
self.playlist_model.createIndex(row, column)
|
||||
)
|
||||
row = edit_index.row()
|
||||
column = edit_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
|
||||
# again calls us here.
|
||||
@ -1025,6 +1103,5 @@ class PlaylistTab(QTableView):
|
||||
def _unmark_as_next(self) -> None:
|
||||
"""Rescan track"""
|
||||
|
||||
model = cast(PlaylistModel, self.model())
|
||||
model.set_next_row(None)
|
||||
self.playlist_model.set_next_row(None)
|
||||
self.clear_selection()
|
||||
|
||||
@ -785,8 +785,6 @@ padding-left: 8px;</string>
|
||||
<string>&Search</string>
|
||||
</property>
|
||||
<addaction name="actionSearch"/>
|
||||
<addaction name="actionFind_next"/>
|
||||
<addaction name="actionFind_previous"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionSelect_next_track"/>
|
||||
<addaction name="actionSelect_previous_track"/>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# Form implementation generated from reading ui file 'app/ui/main_window.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.5.3
|
||||
# Created by: PyQt6 UI code generator 6.6.0
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
@ -492,8 +492,6 @@ class Ui_MainWindow(object):
|
||||
self.menuPlaylist.addAction(self.actionMark_for_moving)
|
||||
self.menuPlaylist.addAction(self.actionPaste)
|
||||
self.menuSearc_h.addAction(self.actionSearch)
|
||||
self.menuSearc_h.addAction(self.actionFind_next)
|
||||
self.menuSearc_h.addAction(self.actionFind_previous)
|
||||
self.menuSearc_h.addSeparator()
|
||||
self.menuSearc_h.addAction(self.actionSelect_next_track)
|
||||
self.menuSearc_h.addAction(self.actionSelect_previous_track)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user