Enable drag-select, then drag selection
This commit is contained in:
parent
32e81fb074
commit
96255e83ea
@ -430,11 +430,6 @@ class PlaylistRows(Base):
|
|||||||
track_id = Column(Integer, ForeignKey('tracks.id'), nullable=True)
|
track_id = Column(Integer, ForeignKey('tracks.id'), nullable=True)
|
||||||
track = relationship("Tracks", back_populates="playlistrows")
|
track = relationship("Tracks", back_populates="playlistrows")
|
||||||
|
|
||||||
# Ensure row numbers are unique within each playlist
|
|
||||||
__table_args__ = (UniqueConstraint
|
|
||||||
('row_number', 'playlist_id', name="uniquerow"),
|
|
||||||
)
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return (
|
return (
|
||||||
f"<PlaylistRow(id={self.id}, playlist_id={self.playlist_id}, "
|
f"<PlaylistRow(id={self.id}, playlist_id={self.playlist_id}, "
|
||||||
|
|||||||
201
app/playlists.py
201
app/playlists.py
@ -9,9 +9,9 @@ from PyQt5.QtCore import Qt
|
|||||||
from PyQt5.QtGui import (
|
from PyQt5.QtGui import (
|
||||||
QColor,
|
QColor,
|
||||||
QFont,
|
QFont,
|
||||||
# QDropEvent
|
QDropEvent
|
||||||
)
|
)
|
||||||
# from PyQt5 import QtWidgets
|
from PyQt5 import QtWidgets
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
QAbstractItemView,
|
QAbstractItemView,
|
||||||
# QApplication,
|
# QApplication,
|
||||||
@ -37,9 +37,8 @@ from helpers import (
|
|||||||
get_relative_date,
|
get_relative_date,
|
||||||
# open_in_audacity
|
# open_in_audacity
|
||||||
)
|
)
|
||||||
# from log import log.debug, log.error
|
from log import log
|
||||||
from models import (
|
from models import (
|
||||||
# Notes,
|
|
||||||
Playdates,
|
Playdates,
|
||||||
Playlists,
|
Playlists,
|
||||||
PlaylistRows,
|
PlaylistRows,
|
||||||
@ -115,9 +114,9 @@ class PlaylistTab(QTableWidget):
|
|||||||
# Set up widget
|
# Set up widget
|
||||||
# self.setEditTriggers(QtWidgets.QAbstractItemView.AllEditTriggers)
|
# self.setEditTriggers(QtWidgets.QAbstractItemView.AllEditTriggers)
|
||||||
self.setAlternatingRowColors(True)
|
self.setAlternatingRowColors(True)
|
||||||
# self.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
|
self.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
|
||||||
# self.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
|
self.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
|
||||||
# self.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
|
self.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
|
||||||
self.setRowCount(0)
|
self.setRowCount(0)
|
||||||
self.setColumnCount(len(columns))
|
self.setColumnCount(len(columns))
|
||||||
|
|
||||||
@ -133,14 +132,12 @@ class PlaylistTab(QTableWidget):
|
|||||||
key=lambda item: item.idx))]
|
key=lambda item: item.idx))]
|
||||||
)
|
)
|
||||||
|
|
||||||
# self.setDragEnabled(True)
|
self.setAcceptDrops(True)
|
||||||
# self.setAcceptDrops(True)
|
self.viewport().setAcceptDrops(True)
|
||||||
# self.viewport().setAcceptDrops(True)
|
self.setDragDropOverwriteMode(False)
|
||||||
# self.setDragDropOverwriteMode(False)
|
self.setDropIndicatorShown(True)
|
||||||
# self.setDropIndicatorShown(True)
|
self.setDragDropMode(QAbstractItemView.InternalMove)
|
||||||
# self.setSelectionMode(QAbstractItemView.ExtendedSelection)
|
self.setDragEnabled(False)
|
||||||
# self.setSelectionBehavior(QAbstractItemView.SelectRows)
|
|
||||||
# self.setDragDropMode(QAbstractItemView.InternalMove)
|
|
||||||
#
|
#
|
||||||
# # This property defines how the widget shows a context menu
|
# # This property defines how the widget shows a context menu
|
||||||
# self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
|
# self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
|
||||||
@ -182,55 +179,66 @@ class PlaylistTab(QTableWidget):
|
|||||||
if record.f_int != self.columnWidth(idx):
|
if record.f_int != self.columnWidth(idx):
|
||||||
record.update(session, {'f_int': width})
|
record.update(session, {'f_int': width})
|
||||||
|
|
||||||
# def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
# return f"<PlaylistTab(id={self.playlist_id}"
|
return f"<PlaylistTab(id={self.playlist_id}"
|
||||||
#
|
#
|
||||||
# # ########## Events ##########
|
# # ########## Events ##########
|
||||||
#
|
#
|
||||||
# def dropEvent(self, event: QDropEvent) -> None:
|
# def closeEditor(self, editor, hint): # review
|
||||||
# # if not event.isAccepted() and event.source() == self:
|
# super(PlaylistTab, self).closeEditor(editor, hint)
|
||||||
# if not event.source() == self:
|
# self.cellEditingEnded.emit()
|
||||||
# return # We don't accept external drops
|
|
||||||
#
|
def dropEvent(self, event: QDropEvent) -> None:
|
||||||
# drop_row: int = self._drop_on(event)
|
"""
|
||||||
#
|
Handle drag/drop of rows
|
||||||
# rows: List = sorted(set(item.row() for item in self.selectedItems()))
|
|
||||||
# rows_to_move = [
|
https://stackoverflow.com/questions/26227885/drag-and-drop-rows-within-qtablewidget
|
||||||
# [QTableWidgetItem(self.item(row_index, column_index)) for
|
"""
|
||||||
# column_index in range(self.columnCount())]
|
|
||||||
# for row_index in rows
|
if not event.source() == self:
|
||||||
# ]
|
return # We don't accept external drops
|
||||||
# for row_index in reversed(rows):
|
|
||||||
# self.removeRow(row_index)
|
drop_row: int = self._drop_on(event)
|
||||||
# if row_index < drop_row:
|
|
||||||
# drop_row -= 1
|
rows: List = sorted(set(item.row() for item in self.selectedItems()))
|
||||||
#
|
rows_to_move = [
|
||||||
# for row_index, data in enumerate(rows_to_move):
|
[QTableWidgetItem(self.item(row_index, column_index)) for
|
||||||
# row_index += drop_row
|
column_index in range(self.columnCount())]
|
||||||
# self.insertRow(row_index)
|
for row_index in rows
|
||||||
# for column_index, column_data in enumerate(data):
|
]
|
||||||
# self.setItem(row_index, column_index, column_data)
|
for row_index in reversed(rows):
|
||||||
# event.accept()
|
self.removeRow(row_index)
|
||||||
# # The above doesn't handle column spans, which we use in note
|
if row_index < drop_row:
|
||||||
# # rows. Check and fix:
|
drop_row -= 1
|
||||||
# row = 0 # So row is defined even if there are no rows in range
|
|
||||||
# for row in range(drop_row, drop_row + len(rows_to_move)):
|
for row_index, data in enumerate(rows_to_move):
|
||||||
# if row in self._get_notes_rows():
|
row_index += drop_row
|
||||||
# self.setSpan(
|
self.insertRow(row_index)
|
||||||
# row, self.COL_NOTE, self.NOTE_ROW_SPAN, self.NOTE_COL_SPAN)
|
for column_index, column_data in enumerate(data):
|
||||||
#
|
self.setItem(row_index, column_index, column_data)
|
||||||
# # Scroll to drop zone
|
event.accept()
|
||||||
# self.scrollToItem(self.item(row, 1))
|
# The above doesn't handle column spans, which we use in note
|
||||||
# super().dropEvent(event)
|
# rows. Check and fix:
|
||||||
#
|
for row in range(drop_row, drop_row + len(rows_to_move)):
|
||||||
# log.debug(
|
if not self._get_row_track_id(row):
|
||||||
# "playlist.dropEvent(): "
|
self.setSpan(row, 1, 1, len(columns))
|
||||||
# f"Moved row(s) {rows} to become row {drop_row}"
|
|
||||||
# )
|
# Scroll to drop zone
|
||||||
#
|
self.scrollToItem(self.item(row, 1))
|
||||||
# with Session() as session: # checked
|
|
||||||
# self.save_playlist(session)
|
# Reset drag mode to allow row selection by dragging
|
||||||
# self.update_display(session)
|
self.setDragEnabled(False)
|
||||||
|
|
||||||
|
super().dropEvent(event)
|
||||||
|
|
||||||
|
log.debug(
|
||||||
|
"playlist.dropEvent(): "
|
||||||
|
f"Moved row(s) {rows} to become row {drop_row}"
|
||||||
|
)
|
||||||
|
|
||||||
|
with Session() as session: # checked
|
||||||
|
self.save_playlist(session)
|
||||||
|
self.update_display(session)
|
||||||
#
|
#
|
||||||
# def edit(self, index, trigger, event): # review
|
# def edit(self, index, trigger, event): # review
|
||||||
# result = super(PlaylistTab, self).edit(index, trigger, event)
|
# result = super(PlaylistTab, self).edit(index, trigger, event)
|
||||||
@ -238,10 +246,6 @@ class PlaylistTab(QTableWidget):
|
|||||||
# self.cellEditingStarted.emit(index.row(), index.column())
|
# self.cellEditingStarted.emit(index.row(), index.column())
|
||||||
# return result
|
# return result
|
||||||
#
|
#
|
||||||
# def closeEditor(self, editor, hint): # review
|
|
||||||
# super(PlaylistTab, self).closeEditor(editor, hint)
|
|
||||||
# self.cellEditingEnded.emit()
|
|
||||||
#
|
|
||||||
# def eventFilter(self, source, event): # review
|
# def eventFilter(self, source, event): # review
|
||||||
# """Used to process context (right-click) menu, which is defined here"""
|
# """Used to process context (right-click) menu, which is defined here"""
|
||||||
#
|
#
|
||||||
@ -287,7 +291,18 @@ class PlaylistTab(QTableWidget):
|
|||||||
# act_delete.triggered.connect(self._delete_rows)
|
# act_delete.triggered.connect(self._delete_rows)
|
||||||
#
|
#
|
||||||
# return super(PlaylistTab, self).eventFilter(source, event)
|
# return super(PlaylistTab, self).eventFilter(source, event)
|
||||||
#
|
|
||||||
|
def mouseReleaseEvent(self, event):
|
||||||
|
"""
|
||||||
|
Enable dragging if rows are selected
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self.selectedItems():
|
||||||
|
self.setDragEnabled(True)
|
||||||
|
else:
|
||||||
|
self.setDragEnabled(False)
|
||||||
|
super().mouseReleaseEvent(event)
|
||||||
|
|
||||||
# # ########## Externally called functions ##########
|
# # ########## Externally called functions ##########
|
||||||
#
|
#
|
||||||
# def closeEvent(self, event) -> None:
|
# def closeEvent(self, event) -> None:
|
||||||
@ -1231,15 +1246,19 @@ class PlaylistTab(QTableWidget):
|
|||||||
#
|
#
|
||||||
# self.save_playlist(session)
|
# self.save_playlist(session)
|
||||||
# self.update_display(session)
|
# self.update_display(session)
|
||||||
#
|
|
||||||
# def _drop_on(self, event): # review
|
def _drop_on(self, event):
|
||||||
# index = self.indexAt(event.pos())
|
"""
|
||||||
# if not index.isValid():
|
https://stackoverflow.com/questions/26227885/drag-and-drop-rows-within-qtablewidget
|
||||||
# return self.rowCount()
|
"""
|
||||||
#
|
|
||||||
# return (index.row() + 1 if self._is_below(event.pos(), index)
|
index = self.indexAt(event.pos())
|
||||||
# else index.row())
|
if not index.isValid():
|
||||||
#
|
return self.rowCount()
|
||||||
|
|
||||||
|
return (index.row() + 1 if self._is_below(event.pos(), index)
|
||||||
|
else index.row())
|
||||||
|
|
||||||
# def _edit_note_cell(self, row, column): # review
|
# def _edit_note_cell(self, row, column): # review
|
||||||
# """Called when table is single-clicked"""
|
# """Called when table is single-clicked"""
|
||||||
#
|
#
|
||||||
@ -1481,20 +1500,24 @@ class PlaylistTab(QTableWidget):
|
|||||||
# if repaint:
|
# if repaint:
|
||||||
# self.save_playlist(session)
|
# self.save_playlist(session)
|
||||||
# self.update_display(session, clear_selection=False)
|
# self.update_display(session, clear_selection=False)
|
||||||
#
|
|
||||||
# def _is_below(self, pos, index): # review
|
def _is_below(self, pos, index): # review
|
||||||
# rect = self.visualRect(index)
|
"""
|
||||||
# margin = 2
|
https://stackoverflow.com/questions/26227885/drag-and-drop-rows-within-qtablewidget
|
||||||
# if pos.y() - rect.top() < margin:
|
"""
|
||||||
# return False
|
|
||||||
# elif rect.bottom() - pos.y() < margin:
|
rect = self.visualRect(index)
|
||||||
# return True
|
margin = 2
|
||||||
# return (
|
if pos.y() - rect.top() < margin:
|
||||||
# rect.contains(pos, True) and not
|
return False
|
||||||
# (int(self.model().flags(index)) & Qt.ItemIsDropEnabled)
|
elif rect.bottom() - pos.y() < margin:
|
||||||
# and pos.y() >= rect.center().y() # noqa W503
|
return True
|
||||||
# )
|
return (
|
||||||
#
|
rect.contains(pos, True) and not
|
||||||
|
(int(self.model().flags(index)) & Qt.ItemIsDropEnabled)
|
||||||
|
and pos.y() >= rect.center().y() # noqa W503
|
||||||
|
)
|
||||||
|
|
||||||
# def _is_note_row(self, row: int) -> bool:
|
# def _is_note_row(self, row: int) -> bool:
|
||||||
# """
|
# """
|
||||||
# Return True if passed row is a note row, else False
|
# Return True if passed row is a note row, else False
|
||||||
|
|||||||
@ -0,0 +1,24 @@
|
|||||||
|
"""Drop uniquerow index on playlist_rows
|
||||||
|
|
||||||
|
Revision ID: 29c0d7ffc741
|
||||||
|
Revises: 3b063011ed67
|
||||||
|
Create Date: 2022-08-06 22:21:46.881378
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '29c0d7ffc741'
|
||||||
|
down_revision = '3b063011ed67'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
op.drop_index('uniquerow', table_name='playlist_rows')
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
op.create_index('uniquerow', 'playlist_rows', ['row_number', 'playlist_id'], unique=True)
|
||||||
Loading…
Reference in New Issue
Block a user