Compare commits

..

No commits in common. "9a7d24b8957d97d6a2544bf3ba84a951ee88b3dd" and "fed4e9fbdefb6532179695369e6b42c5997ae349" have entirely different histories.

4 changed files with 52 additions and 197 deletions

View File

@ -45,9 +45,11 @@ def Session() -> Generator[scoped_session, None, None]:
function = frame.function function = frame.function
lineno = frame.lineno lineno = frame.lineno
Session = scoped_session(sessionmaker(bind=engine, future=True)) Session = scoped_session(sessionmaker(bind=engine, future=True))
log.debug(f"SqlA: session acquired [{hex(id(Session))}]") log.debug(
log.debug(f"Session acquisition: {function}:{lineno} [{hex(id(Session))}]") f"Session acquired, {file=}, {function=}, "
f"function{lineno=}, {Session=}"
)
yield Session yield Session
log.debug(f" SqlA: session released [{hex(id(Session))}]") log.debug(" Session released")
Session.commit() Session.commit()
Session.close() Session.close()

View File

@ -249,17 +249,6 @@ class ImportTrack(QObject):
self.finished.emit(self.playlist) self.finished.emit(self.playlist)
class MusicMusterSignals(QObject):
"""
Class for all MusicMuster signals. See:
- https://zetcode.com/gui/pyqt5/eventssignals/
- https://stackoverflow.com/questions/62654525/
emit-a-signal-from-another-class-to-main-class
"""
save_playlist_signal = pyqtSignal()
class Window(QMainWindow, Ui_MainWindow): class Window(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None, *args, **kwargs) -> None: def __init__(self, parent=None, *args, **kwargs) -> None:
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
@ -278,8 +267,6 @@ class Window(QMainWindow, Ui_MainWindow):
self.previous_track_position: Optional[float] = None self.previous_track_position: Optional[float] = None
self.selected_plrs: Optional[List[PlaylistRows]] = None self.selected_plrs: Optional[List[PlaylistRows]] = None
self.signals = MusicMusterSignals()
# Set colours that will be used by playlist row stripes # Set colours that will be used by playlist row stripes
palette = QPalette() palette = QPalette()
palette.setColor(QPalette.Base, QColor(Config.COLOUR_EVEN_PLAYLIST)) palette.setColor(QPalette.Base, QColor(Config.COLOUR_EVEN_PLAYLIST))
@ -527,9 +514,11 @@ class Window(QMainWindow, Ui_MainWindow):
"Can't close current track playlist", 5000) "Can't close current track playlist", 5000)
return False return False
# Attempt to close next track playlist # Don't close next track playlist
if self.tabPlaylist.widget(tab_index) == self.next_track.playlist_tab: if self.tabPlaylist.widget(tab_index) == self.next_track.playlist_tab:
self.next_track.playlist_tab.mark_unnext() self.statusbar.showMessage(
"Can't close next track playlist", 5000)
return False
# Record playlist as closed and update remaining playlist tabs # Record playlist as closed and update remaining playlist tabs
with Session() as session: with Session() as session:
@ -625,8 +614,7 @@ class Window(QMainWindow, Ui_MainWindow):
assert playlist.id assert playlist.id
playlist_tab = PlaylistTab( playlist_tab = PlaylistTab(
musicmuster=self, session=session, playlist_id=playlist.id, musicmuster=self, session=session, playlist_id=playlist.id)
signals=self.signals)
idx = self.tabPlaylist.addTab(playlist_tab, playlist.name) idx = self.tabPlaylist.addTab(playlist_tab, playlist.name)
self.tabPlaylist.setCurrentIndex(idx) self.tabPlaylist.setCurrentIndex(idx)
@ -646,8 +634,6 @@ class Window(QMainWindow, Ui_MainWindow):
def debug(self): def debug(self):
"""Invoke debugger""" """Invoke debugger"""
visible_playlist_id = self.visible_playlist_tab().playlist_id
print(f"Active playlist id={visible_playlist_id}")
import ipdb # type: ignore import ipdb # type: ignore
ipdb.set_trace() ipdb.set_trace()

View File

@ -8,6 +8,7 @@ from datetime import datetime, timedelta
from typing import cast, List, Optional, TYPE_CHECKING, Union from typing import cast, List, Optional, TYPE_CHECKING, Union
from PyQt5.QtCore import ( from PyQt5.QtCore import (
pyqtSignal,
QEvent, QEvent,
QModelIndex, QModelIndex,
QObject, QObject,
@ -59,7 +60,7 @@ from models import (
) )
if TYPE_CHECKING: if TYPE_CHECKING:
from musicmuster import Window, MusicMusterSignals from musicmuster import Window
start_time_re = re.compile(r"@\d\d:\d\d:\d\d") start_time_re = re.compile(r"@\d\d:\d\d:\d\d")
HEADER_NOTES_COLUMN = 2 HEADER_NOTES_COLUMN = 2
@ -137,11 +138,10 @@ class PlaylistTab(QTableWidget):
def __init__(self, musicmuster: "Window", def __init__(self, musicmuster: "Window",
session: scoped_session, session: scoped_session,
playlist_id: int, signals: "MusicMusterSignals") -> None: playlist_id: int, *args, **kwargs) -> None:
super().__init__() super().__init__(*args, **kwargs)
self.musicmuster: Window = musicmuster self.musicmuster: Window = musicmuster
self.playlist_id = playlist_id self.playlist_id = playlist_id
self.signals = signals
# Set up widget # Set up widget
self.menu: Optional[QMenu] = None self.menu: Optional[QMenu] = None
@ -170,7 +170,6 @@ class PlaylistTab(QTableWidget):
self.horizontalHeader().sectionResized.connect( self.horizontalHeader().sectionResized.connect(
self.resizeRowsToContents) self.resizeRowsToContents)
# Drag and drop setup
self.setAcceptDrops(True) self.setAcceptDrops(True)
self.viewport().setAcceptDrops(True) self.viewport().setAcceptDrops(True)
self.setDragDropOverwriteMode(False) self.setDragDropOverwriteMode(False)
@ -194,7 +193,6 @@ class PlaylistTab(QTableWidget):
self.selecting_in_progress = False self.selecting_in_progress = False
# Connect signals # Connect signals
self.horizontalHeader().sectionResized.connect(self._column_resize) self.horizontalHeader().sectionResized.connect(self._column_resize)
self.signals.save_playlist_signal.connect(self._deferred_save)
# Load playlist rows # Load playlist rows
self.populate_display(session, self.playlist_id) self.populate_display(session, self.playlist_id)
@ -292,7 +290,7 @@ class PlaylistTab(QTableWidget):
act_unnext = self.menu.addAction( act_unnext = self.menu.addAction(
"Unmark as next track") "Unmark as next track")
act_unnext.triggered.connect( act_unnext.triggered.connect(
lambda: self.mark_unnext()) lambda: self._mark_unnext(row_number))
if sep: if sep:
self.menu.addSeparator() self.menu.addSeparator()
@ -442,7 +440,7 @@ class PlaylistTab(QTableWidget):
track_id = self._get_row_track_id(row) track_id = self._get_row_track_id(row)
# Determine cell type changed # Determin cell type changed
with Session() as session: with Session() as session:
# Get playlistrow object # Get playlistrow object
plr_id = self._get_playlistrow_id(row) plr_id = self._get_playlistrow_id(row)
@ -462,12 +460,8 @@ class PlaylistTab(QTableWidget):
self._set_row_start_time(row, start_time) self._set_row_start_time(row, start_time)
else: else:
self._set_row_start_time(row, None) self._set_row_start_time(row, None)
# Update note display # Update display including note colour
self._set_row_note(session, row, new_text) self._set_row_note(session, row, new_text)
# If this is a header row, ecalcuate track times in case
# note added a start time
if not track_id:
self._update_start_end_times()
else: else:
track = None track = None
if track_id: if track_id:
@ -599,14 +593,14 @@ class PlaylistTab(QTableWidget):
return [self._get_playlistrow_id(a) for a in self._get_selected_rows()] return [self._get_playlistrow_id(a) for a in self._get_selected_rows()]
def get_selected_playlistrows( def get_selected_playlistrows(
self, session: scoped_session) -> List[PlaylistRows]: self, session: scoped_session) -> Optional[List[PlaylistRows]]:
""" """
Return a list of PlaylistRows of the selected rows Return a list of PlaylistRows of the selected rows
""" """
plr_ids = self.get_selected_playlistrow_ids() plr_ids = self.get_selected_playlistrow_ids()
if not plr_ids: if not plr_ids:
return [] return None
plrs = [session.get(PlaylistRows, a) for a in plr_ids] plrs = [session.get(PlaylistRows, a) for a in plr_ids]
return [plr for plr in plrs if plr is not None] return [plr for plr in plrs if plr is not None]
@ -706,8 +700,7 @@ class PlaylistTab(QTableWidget):
_ = self._set_row_userdata(row, self.ROW_TRACK_ID, 0) _ = self._set_row_userdata(row, self.ROW_TRACK_ID, 0)
if update_track_times: if update_track_times:
# Queue time updates so playlist updates first self._update_start_end_times()
QTimer.singleShot(0, lambda: self._update_start_end_times())
def insert_track(self, session: scoped_session, track: Tracks, def insert_track(self, session: scoped_session, track: Tracks,
note: Optional[str] = None, repaint: bool = True) -> None: note: Optional[str] = None, repaint: bool = True) -> None:
@ -750,19 +743,6 @@ class PlaylistTab(QTableWidget):
# Let display update, then save playlist # Let display update, then save playlist
QTimer.singleShot(0, lambda: self.save_playlist(session)) QTimer.singleShot(0, lambda: self.save_playlist(session))
def mark_unnext(self) -> None:
"""
Unmark passed row as next track
"""
row = self._get_next_track_row_number()
if not row:
return
self.musicmuster.clear_next()
self.clear_selection()
self._set_row_colour(row, None)
self.musicmuster.update_headers()
def play_started(self, session: scoped_session) -> None: def play_started(self, session: scoped_session) -> None:
""" """
Notification from musicmuster that track has started playing. Notification from musicmuster that track has started playing.
@ -853,6 +833,13 @@ class PlaylistTab(QTableWidget):
for row in sorted(row_numbers, reverse=True): for row in sorted(row_numbers, reverse=True):
self.removeRow(row) self.removeRow(row)
def remove_selected_rows(self) -> None:
"""Remove selected rows from display"""
self.remove_rows(self._get_selected_rows())
# Reset drag mode
self.setDragEnabled(False)
def reset_plr_row_colour(self, plr_id: int) -> None: def reset_plr_row_colour(self, plr_id: int) -> None:
"""Reset background of row pointed to by plr_id""" """Reset background of row pointed to by plr_id"""
@ -1305,15 +1292,6 @@ class PlaylistTab(QTableWidget):
cb.clear(mode=cb.Clipboard) cb.clear(mode=cb.Clipboard)
cb.setText(pathqs, mode=cb.Clipboard) cb.setText(pathqs, mode=cb.Clipboard)
def _deferred_save(self) -> None:
"""
Create session and save playlist
"""
print("_deferred_save() called")
with Session() as session:
self.save_playlist(session)
def _delete_rows(self) -> None: def _delete_rows(self) -> None:
""" """
Delete mutliple rows Delete mutliple rows
@ -1323,33 +1301,22 @@ class PlaylistTab(QTableWidget):
- Save the playlist - Save the playlist
""" """
# Delete rows from database
plr_ids = self.get_selected_playlistrow_ids()
if not plr_ids:
return
# Get confirmation
row_count = len(plr_ids)
plural = 's' if row_count > 1 else ''
if not ask_yes_no("Delete rows",
f"Really delete {row_count} row{plural}?"):
return
self.remove_selected_rows()
with Session() as session: with Session() as session:
plrs = self.get_selected_playlistrows(session) QTimer.singleShot(0, lambda: self.save_playlist(session))
row_count = len(plrs)
if not row_count:
return
# Get confirmation
plural = 's' if row_count > 1 else ''
if not ask_yes_no("Delete rows",
f"Really delete {row_count} row{plural}?"):
return
rows_to_delete = [a.row_number for a in plrs]
# Delete rows from database. Would be more efficient to
# query then have a single delete.
for plr in plrs:
session.delete(plr)
# Remove from display
self.remove_rows(rows_to_delete)
# Reset drag mode
self.setDragEnabled(False)
# QTimer.singleShot(0, lambda: self._deferred_save())
self.signals.save_playlist_signal.emit()
def _drop_on(self, event): def _drop_on(self, event):
""" """
@ -1651,6 +1618,16 @@ class PlaylistTab(QTableWidget):
and pos.y() >= rect.center().y() # noqa W503 and pos.y() >= rect.center().y() # noqa W503
) )
def _mark_unnext(self, row: int) -> None:
"""
Unmark passed row as next track
"""
self.musicmuster.clear_next()
self.clear_selection()
self._set_row_colour(row, None)
self.musicmuster.update_headers()
def _mark_unplayed(self, plr: PlaylistRows) -> None: def _mark_unplayed(self, plr: PlaylistRows) -> None:
""" """
Mark passed row as unplayed in this playlist Mark passed row as unplayed in this playlist

110
tree.py
View File

@ -1,110 +0,0 @@
#!/usr/bin/env python
from PyQt5 import QtCore, QtGui, QtWidgets
datas = {
"Category 1": [
("New Game 2", "Playnite", "", "", "Never", "Not Played", ""),
("New Game 3", "Playnite", "", "", "Never", "Not Played", ""),
],
"No Category": [
("New Game", "Playnite", "", "", "Never", "Not Plated", ""),
]
}
class GroupDelegate(QtWidgets.QStyledItemDelegate):
def __init__(self, parent=None):
super(GroupDelegate, self).__init__(parent)
self._plus_icon = QtGui.QIcon("plus.png")
self._minus_icon = QtGui.QIcon("minus.png")
def initStyleOption(self, option, index):
super(GroupDelegate, self).initStyleOption(option, index)
if not index.parent().isValid():
is_open = bool(option.state & QtWidgets.QStyle.State_Open)
option.features |= QtWidgets.QStyleOptionViewItem.HasDecoration
option.icon = self._minus_icon if is_open else self._plus_icon
class GroupView(QtWidgets.QTreeView):
def __init__(self, model, parent=None):
super(GroupView, self).__init__(parent)
self.setIndentation(0)
self.setExpandsOnDoubleClick(False)
self.clicked.connect(self.on_clicked)
delegate = GroupDelegate(self)
self.setItemDelegateForColumn(0, delegate)
self.setModel(model)
self.header().setSectionResizeMode(0, QtWidgets.QHeaderView.ResizeToContents)
self.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
self.setStyleSheet("background-color: #0D1225;")
@QtCore.pyqtSlot(QtCore.QModelIndex)
def on_clicked(self, index):
if not index.parent().isValid() and index.column() == 0:
self.setExpanded(index, not self.isExpanded(index))
class GroupModel(QtGui.QStandardItemModel):
def __init__(self, parent=None):
super(GroupModel, self).__init__(parent)
self.setColumnCount(8)
self.setHorizontalHeaderLabels(["", "Name", "Library", "Release Date", "Genre(s)", "Last Played", "Time Played", ""])
for i in range(self.columnCount()):
it = self.horizontalHeaderItem(i)
it.setForeground(QtGui.QColor("#F2F2F2"))
def add_group(self, group_name):
item_root = QtGui.QStandardItem()
item_root.setEditable(False)
item = QtGui.QStandardItem(group_name)
item.setEditable(False)
ii = self.invisibleRootItem()
i = ii.rowCount()
for j, it in enumerate((item_root, item)):
ii.setChild(i, j, it)
ii.setEditable(False)
for j in range(self.columnCount()):
it = ii.child(i, j)
if it is None:
it = QtGui.QStandardItem()
ii.setChild(i, j, it)
it.setBackground(QtGui.QColor("#002842"))
it.setForeground(QtGui.QColor("#F2F2F2"))
return item_root
def append_element_to_group(self, group_item, texts):
j = group_item.rowCount()
item_icon = QtGui.QStandardItem()
item_icon.setEditable(False)
item_icon.setIcon(QtGui.QIcon("game.png"))
item_icon.setBackground(QtGui.QColor("#0D1225"))
group_item.setChild(j, 0, item_icon)
for i, text in enumerate(texts):
item = QtGui.QStandardItem(text)
item.setEditable(False)
item.setBackground(QtGui.QColor("#0D1225"))
item.setForeground(QtGui.QColor("#F2F2F2"))
group_item.setChild(j, i+1, item)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
model = GroupModel(self)
tree_view = GroupView(model)
self.setCentralWidget(tree_view)
for group, childrens in datas.items():
group_item = model.add_group(group)
for children in childrens:
model.append_element_to_group(group_item, children)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.resize(720, 240)
w.show()
sys.exit(app.exec_())