Compare commits

..

7 Commits

Author SHA1 Message Date
Keith Edmunds
dfb45dd0ff WIP V3: Don't hide next/current row 2023-11-27 11:52:29 +00:00
Keith Edmunds
02391f04b1 WIP V3: hide played tracks working 2023-11-27 11:27:25 +00:00
Keith Edmunds
31f7122a7f WIP V3: fixup tests from earlier changes 2023-11-26 15:27:14 +00:00
Keith Edmunds
480c832852 WIP V3: implement searching with QSortFilterProxyModel (ooo!) 2023-11-26 15:22:01 +00:00
Keith Edmunds
6f5c371510 Git ignore tmp directory 2023-11-25 18:02:39 +00:00
Keith Edmunds
23a9eff43b WIP V3 wire in QSortFilterProxyModel 2023-11-23 18:28:10 +00:00
Keith Edmunds
25e3be6fae WIP V3: add track to header working 2023-11-23 17:12:03 +00:00
7 changed files with 328 additions and 100 deletions

1
.gitignore vendored
View File

@ -10,3 +10,4 @@ StudioPlaylist.png
*.otl
*.howto
.direnv
tmp/

View File

@ -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)
@ -770,14 +762,13 @@ class Window(QMainWindow, Ui_MainWindow):
if self.hide_played_tracks:
self.hide_played_tracks = False
self.active_model().hide_played_tracks(False)
self.btnHidePlayed.setText("Hide played")
else:
self.hide_played_tracks = True
self.active_model().hide_played_tracks(True)
self.btnHidePlayed.setText("Show played")
# Update displayed playlist
self.active_tab().hide_or_show_played_tracks()
def import_track(self) -> None:
"""Import track file"""
@ -870,7 +861,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 +1240,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"""

View File

@ -3,11 +3,13 @@ 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,
)
@ -23,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,
)
@ -122,6 +122,7 @@ class PlaylistModel(QAbstractTableModel):
self.playlist_rows: dict[int, PlaylistRowData] = {}
self.start_end_times: dict[int, StartEndTimes] = {}
self.signals = MusicMusterSignals()
self.played_tracks_hidden = False
self.signals.add_track_to_header_signal.connect(self.add_track_to_header)
self.signals.add_track_to_playlist_signal.connect(self.add_track)
@ -666,6 +667,16 @@ class PlaylistModel(QAbstractTableModel):
return prd.note
def hide_played_tracks(self, hide: bool) -> None:
"""
Set played tracks hidden according to 'hide'
"""
self.played_tracks_hidden = hide
for row_number in range(len(self.playlist_rows)):
if self.is_played_row(row_number):
self.invalidate_row(row_number)
def is_header_row(self, row_number: int) -> bool:
"""
Return True if row is a header row, else False
@ -673,7 +684,7 @@ class PlaylistModel(QAbstractTableModel):
return self.playlist_rows[row_number].path == ""
def is_unplayed_row(self, row_number: int) -> bool:
def is_played_row(self, row_number: int) -> bool:
"""
Return True if row is an unplayed track row, else False
"""
@ -685,9 +696,9 @@ class PlaylistModel(QAbstractTableModel):
proposed_row_number: Optional[int],
track_id: Optional[int] = None,
note: Optional[str] = None,
) -> PlaylistRows:
) -> None:
"""
Insert a track row.
Insert a row.
"""
new_row_number = self._get_new_row_number(proposed_row_number)
@ -704,9 +715,7 @@ class PlaylistModel(QAbstractTableModel):
super().endInsertRows()
self.row_order_changed(self.playlist_id)
self.invalidate_rows(list(range(plr.plr_rownum, len(self.playlist_rows))))
return plr
self.invalidate_rows(list(range(new_row_number, len(self.playlist_rows))))
def invalidate_row(self, modified_row: int) -> None:
"""
@ -714,7 +723,7 @@ class PlaylistModel(QAbstractTableModel):
"""
self.dataChanged.emit(
self.index(modified_row, 0), self.index(modified_row, self.columnCount())
self.index(modified_row, 0), self.index(modified_row, self.columnCount() - 1)
)
def invalidate_rows(self, modified_rows: List[int]) -> None:
@ -986,7 +995,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
@ -1020,12 +1031,14 @@ class PlaylistModel(QAbstractTableModel):
Set row_number as next track. If row_number is None, clear next track.
"""
if row_number is None:
next_row_was = track_sequence.next.plr_rownum
if next_row_was is not None:
self.invalidate_row(next_row_was)
if row_number is None:
if next_row_was is None:
return
track_sequence.next = PlaylistTrack()
self.invalidate_row(next_row_was)
self.signals.next_track_changed_signal.emit()
return
@ -1227,3 +1240,141 @@ 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 filterAcceptsRow(self, source_row: int, source_parent: QModelIndex) -> bool:
"""
Subclass to filter by played status
"""
if self.playlist_model.played_tracks_hidden:
if self.playlist_model.is_played_row(source_row):
# Don't hide current or next track
with Session() as session:
next_plr = session.get(PlaylistRows, track_sequence.next.plr_id)
if (
next_plr
and next_plr.plr_rownum == source_row
and next_plr.playlist_id == self.playlist_model.playlist_id
):
return True
now_plr = session.get(PlaylistRows, track_sequence.now.plr_id)
if (
now_plr
and now_plr.plr_rownum == source_row
and now_plr.playlist_id == self.playlist_model.playlist_id
):
return True
return False
return super().filterAcceptsRow(source_row, source_parent)
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 current_track_started(self):
return self.playlist_model.current_track_started()
def delete_rows(self, row_numbers: List[int]) -> None:
return self.playlist_model.delete_rows(row_numbers)
def get_duplicate_rows(self) -> List[int]:
return self.playlist_model.get_duplicate_rows()
def get_rows_duration(self, row_numbers: List[int]) -> int:
return self.playlist_model.get_rows_duration(row_numbers)
def get_row_info(self, row_number: int) -> PlaylistRowData:
return self.playlist_model.get_row_info(row_number)
def get_row_track_path(self, row_number: int) -> str:
return self.playlist_model.get_row_track_path(row_number)
def hide_played_tracks(self, hide: bool) -> None:
return self.playlist_model.hide_played_tracks(hide)
def insert_row(
self,
proposed_row_number: Optional[int],
track_id: Optional[int] = None,
note: Optional[str] = None,
) -> None:
return self.playlist_model.insert_row(proposed_row_number, track_id, note)
def is_header_row(self, row_number: int) -> bool:
return self.playlist_model.is_header_row(row_number)
def is_played_row(self, row_number: int) -> bool:
return self.playlist_model.is_played_row(row_number)
def mark_unplayed(self, row_numbers: List[int]) -> None:
return self.playlist_model.mark_unplayed(row_numbers)
def move_rows(self, from_rows: List[int], to_row_number: int) -> None:
return self.playlist_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.playlist_model.move_rows_between_playlists(
from_rows, to_row_number, to_playlist_id
)
def open_in_audacity(self, row_number: int) -> None:
return self.playlist_model.open_in_audacity(row_number)
def previous_track_ended(self) -> None:
return self.playlist_model.previous_track_ended()
def remove_track(self, row_number: int) -> None:
return self.playlist_model.remove_track(row_number)
def rescan_track(self, row_number: int) -> None:
return self.playlist_model.rescan_track(row_number)
def set_next_row(self, row_number: Optional[int]) -> None:
return self.playlist_model.set_next_row(row_number)
def sort_by_artist(self, row_numbers: List[int]) -> None:
return self.playlist_model.sort_by_artist(row_numbers)
def sort_by_duration(self, row_numbers: List[int]) -> None:
return self.playlist_model.sort_by_duration(row_numbers)
def sort_by_lastplayed(self, row_numbers: List[int]) -> None:
return self.playlist_model.sort_by_lastplayed(row_numbers)
def sort_by_title(self, row_numbers: List[int]) -> None:
return self.playlist_model.sort_by_title(row_numbers)
def update_track_times(self) -> None:
return self.playlist_model.update_track_times()

View File

@ -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,13 +46,14 @@ from helpers import (
open_in_audacity,
send_mail,
set_track_metadata,
show_warning,
)
from log import log
from models import PlaylistRows, Settings, Tracks, NoteColours
if TYPE_CHECKING:
from musicmuster import Window
from playlistmodel import PlaylistModel
from playlistmodel import PlaylistModel, PlaylistProxyModel
# HEADER_NOTES_COLUMN = 2
@ -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(PlaylistModel(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())
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,22 +543,25 @@ 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 ##########
def _add_track(self, row_number: int) -> None:
def _add_track(self) -> None:
"""Add a track to a section header making it a normal track row"""
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,
)
@ -498,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()
@ -528,21 +607,21 @@ 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)
# TODO
if header_row:
self._add_context_menu("Add a track", lambda: print("Add a track"))
self._add_context_menu("Add a track", lambda: self._add_track())
# # ----------------------
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())
"Mark unplayed",
lambda: self._mark_as_unplayed(self.get_selected_rows()),
)
# Unmark as next
@ -579,11 +658,13 @@ 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))
self._add_context_menu(
"Copy track path", lambda: self._copy_path(model_row_number)
)
def _calculate_end_time(
self, start: Optional[datetime], duration: int
@ -625,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
@ -663,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"""
@ -676,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"
@ -733,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:
@ -780,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:
@ -925,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)
@ -945,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)}"
@ -1005,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.
@ -1019,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()

View File

@ -785,8 +785,6 @@ padding-left: 8px;</string>
<string>&amp;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"/>

View File

@ -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)

View File

@ -43,8 +43,7 @@ def create_model_with_playlist_rows(
# Create a model
model = playlistmodel.PlaylistModel(playlist.id)
for row in range(rows):
plr = model.insert_row(proposed_row_number=row, note=str(row))
model.playlist_rows[plr.plr_rownum] = playlistmodel.PlaylistRowData(plr)
model.insert_row(proposed_row_number=row, note=str(row))
session.commit()
return model