Expang edit box working, code untidy
This commit is contained in:
parent
eaac2ef4ca
commit
4860c9f188
170
app/playlists.py
170
app/playlists.py
@ -8,15 +8,17 @@ from PyQt6.QtCore import (
|
||||
QModelIndex,
|
||||
QObject,
|
||||
QItemSelection,
|
||||
QSize,
|
||||
Qt,
|
||||
QTimer,
|
||||
)
|
||||
from PyQt6.QtGui import QAction, QKeyEvent
|
||||
from PyQt6.QtGui import QAction, QKeyEvent, QTextDocument
|
||||
from PyQt6.QtWidgets import (
|
||||
QAbstractItemDelegate,
|
||||
QAbstractItemView,
|
||||
QApplication,
|
||||
QDoubleSpinBox,
|
||||
QFrame,
|
||||
QHeaderView,
|
||||
QMenu,
|
||||
QMessageBox,
|
||||
@ -28,6 +30,7 @@ from PyQt6.QtWidgets import (
|
||||
QStyleOptionViewItem,
|
||||
QTableView,
|
||||
QTableWidgetItem,
|
||||
QTextEdit,
|
||||
QWidget,
|
||||
)
|
||||
|
||||
@ -58,54 +61,119 @@ class EscapeDelegate(QStyledItemDelegate):
|
||||
- closes the edit on control-return
|
||||
- checks with user before abandoning edit on Escape
|
||||
- positions cursor where double-click occurs
|
||||
|
||||
Parts inspired by https://stackoverflow.com/questions/69113867/
|
||||
make-row-of-qtableview-expand-as-editor-grows-in-height
|
||||
"""
|
||||
|
||||
class EditorDocument(QTextDocument):
|
||||
def __init__(self, parent):
|
||||
super().__init__(parent)
|
||||
self.setDocumentMargin(0)
|
||||
self.contentsChange.connect(self.contents_change)
|
||||
self.height = None
|
||||
parent.setDocument(self)
|
||||
|
||||
def contents_change(self, position, chars_removed, chars_added):
|
||||
def resize_func():
|
||||
if self.size().height() != self.height:
|
||||
doc_size = self.size()
|
||||
self.parent().resize(int(doc_size.width()), int(doc_size.height()))
|
||||
|
||||
QTimer.singleShot(0, resize_func)
|
||||
|
||||
def __init__(self, parent: QWidget, source_model: PlaylistModel) -> None:
|
||||
super().__init__(parent)
|
||||
self.source_model = source_model
|
||||
self.signals = MusicMusterSignals()
|
||||
self.click_position = None # Store the mouse click position
|
||||
self.current_editor = None
|
||||
|
||||
def createEditor(
|
||||
self,
|
||||
parent: Optional[QWidget],
|
||||
option: QStyleOptionViewItem,
|
||||
index: QModelIndex,
|
||||
) -> Optional[QDoubleSpinBox | QPlainTextEdit]:
|
||||
) -> Optional[QDoubleSpinBox | QTextEdit]:
|
||||
"""
|
||||
Intercept createEditor call and make row just a little bit taller
|
||||
"""
|
||||
|
||||
editor: QDoubleSpinBox | QPlainTextEdit
|
||||
# editor: QDoubleSpinBox | QTextEdit
|
||||
|
||||
class Editor(QTextEdit):
|
||||
def resizeEvent(self, event):
|
||||
super().resizeEvent(event)
|
||||
parent.parent().resizeRowToContents(index.row())
|
||||
|
||||
def keyPressEvent(self, event):
|
||||
if event.modifiers() == Qt.KeyboardModifier.ControlModifier:
|
||||
if event.key() == Qt.Key.Key_Return:
|
||||
self.commitData.emit(editor)
|
||||
# self.closeEditor.emit(editor)
|
||||
return
|
||||
# elif event.key() == Qt.Key.Key_Escape:
|
||||
# # Close editor if no changes have been made
|
||||
# data_modified = False
|
||||
# if isinstance(editor, QPlainTextEdit):
|
||||
# data_modified = self.original_model_data != editor.toPlainText()
|
||||
# elif isinstance(editor, QDoubleSpinBox):
|
||||
# data_modified = (
|
||||
# self.original_model_data != int(editor.value()) * 1000
|
||||
# )
|
||||
# if not data_modified:
|
||||
# self.closeEditor.emit(editor)
|
||||
# return True
|
||||
|
||||
# discard_edits = QMessageBox.question(
|
||||
# cast(QWidget, self.parent()), "Abandon edit", "Discard changes?"
|
||||
# )
|
||||
# if discard_edits == QMessageBox.StandardButton.Yes:
|
||||
# self.closeEditor.emit(editor)
|
||||
# return True
|
||||
|
||||
super().keyPressEvent(event)
|
||||
|
||||
self.signals = MusicMusterSignals()
|
||||
self.signals.enable_escape_signal.emit(False)
|
||||
|
||||
if isinstance(self.parent(), PlaylistTab):
|
||||
p = cast(PlaylistTab, self.parent())
|
||||
# if isinstance(self.parent(), PlaylistTab):
|
||||
# p = cast(PlaylistTab, self.parent())
|
||||
|
||||
if index.column() == Col.INTRO.value:
|
||||
editor = QDoubleSpinBox(parent)
|
||||
editor.setDecimals(1)
|
||||
editor.setSingleStep(0.1)
|
||||
return editor
|
||||
elif isinstance(index.data(), str):
|
||||
editor = QPlainTextEdit(parent)
|
||||
editor.setGeometry(option.rect) # Match the cell geometry
|
||||
row = index.row()
|
||||
row_height = p.rowHeight(row)
|
||||
p.setRowHeight(row, row_height + Config.MINIMUM_ROW_HEIGHT)
|
||||
return editor
|
||||
# if index.column() == Col.INTRO.value:
|
||||
# editor = QDoubleSpinBox(parent)
|
||||
# editor.setDecimals(1)
|
||||
# editor.setSingleStep(0.1)
|
||||
# return editor
|
||||
# elif isinstance(index.data(), str):
|
||||
if self.current_editor:
|
||||
editor = self.current_editor
|
||||
else:
|
||||
editor = Editor(parent)
|
||||
editor.setVerticalScrollBarPolicy(
|
||||
Qt.ScrollBarPolicy.ScrollBarAlwaysOff
|
||||
)
|
||||
editor.setFrameShape(QFrame.Shape.NoFrame)
|
||||
self.current_editor = editor
|
||||
# # editor.setGeometry(option.rect) # Match the cell geometry
|
||||
# row = index.row()
|
||||
# row_height = p.rowHeight(row)
|
||||
# p.setRowHeight(row, row_height + Config.MINIMUM_ROW_HEIGHT)
|
||||
|
||||
return super().createEditor(parent, option, index)
|
||||
EscapeDelegate.EditorDocument(editor)
|
||||
return editor
|
||||
|
||||
# return super().createEditor(parent, option, index)
|
||||
|
||||
def destroyEditor(self, editor: Optional[QWidget], index: QModelIndex) -> None:
|
||||
"""
|
||||
Intercept editor destroyment
|
||||
"""
|
||||
|
||||
super().destroyEditor(editor, index)
|
||||
self.current_editor = None
|
||||
self.parent().resizeRowToContents(index.row())
|
||||
self.signals.enable_escape_signal.emit(True)
|
||||
return super().destroyEditor(editor, index)
|
||||
|
||||
def editorEvent(
|
||||
self, event: QEvent, model: QAbstractItemModel, option, index: QModelIndex
|
||||
@ -123,7 +191,7 @@ class EscapeDelegate(QStyledItemDelegate):
|
||||
return super().eventFilter(editor, event)
|
||||
|
||||
if event.type() == QEvent.Type.Show:
|
||||
if self.click_position and isinstance(editor, QPlainTextEdit):
|
||||
if self.click_position and isinstance(editor, QTextEdit):
|
||||
# Map click position to editor's local space
|
||||
local_click_position = editor.mapFromParent(self.click_position)
|
||||
|
||||
@ -136,36 +204,20 @@ class EscapeDelegate(QStyledItemDelegate):
|
||||
|
||||
return super().eventFilter(editor, event)
|
||||
|
||||
elif event.type() == QEvent.Type.KeyPress:
|
||||
key_event = cast(QKeyEvent, event)
|
||||
if key_event.key() == Qt.Key.Key_Return:
|
||||
if key_event.modifiers() == (Qt.KeyboardModifier.ControlModifier):
|
||||
self.commitData.emit(editor)
|
||||
self.closeEditor.emit(editor)
|
||||
return True
|
||||
|
||||
elif key_event.key() == Qt.Key.Key_Escape:
|
||||
# Close editor if no changes have been made
|
||||
data_modified = False
|
||||
if isinstance(editor, QPlainTextEdit):
|
||||
data_modified = self.original_model_data != editor.toPlainText()
|
||||
elif isinstance(editor, QDoubleSpinBox):
|
||||
data_modified = (
|
||||
self.original_model_data != int(editor.value()) * 1000
|
||||
)
|
||||
if not data_modified:
|
||||
self.closeEditor.emit(editor)
|
||||
return True
|
||||
|
||||
discard_edits = QMessageBox.question(
|
||||
cast(QWidget, self.parent()), "Abandon edit", "Discard changes?"
|
||||
)
|
||||
if discard_edits == QMessageBox.StandardButton.Yes:
|
||||
self.closeEditor.emit(editor)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def sizeHint(self, option, index):
|
||||
self.initStyleOption(option, index)
|
||||
if self.current_editor:
|
||||
doc = self.current_editor.document()
|
||||
else:
|
||||
doc = QTextDocument()
|
||||
doc.setTextWidth(option.rect.width())
|
||||
doc.setDefaultFont(option.font)
|
||||
doc.setDocumentMargin(0)
|
||||
doc.setHtml(option.text)
|
||||
return QSize(int(doc.idealWidth()), int(doc.size().height()))
|
||||
|
||||
def setEditorData(self, editor, index):
|
||||
proxy_model = index.model()
|
||||
edit_index = proxy_model.mapToSource(index)
|
||||
@ -182,7 +234,7 @@ class EscapeDelegate(QStyledItemDelegate):
|
||||
proxy_model = index.model()
|
||||
edit_index = proxy_model.mapToSource(index)
|
||||
|
||||
if isinstance(editor, QPlainTextEdit):
|
||||
if isinstance(editor, QTextEdit):
|
||||
value = editor.toPlainText().strip()
|
||||
elif isinstance(editor, QDoubleSpinBox):
|
||||
value = editor.value()
|
||||
@ -264,7 +316,7 @@ class PlaylistTab(QTableView):
|
||||
|
||||
# Set up for Audacity
|
||||
try:
|
||||
self.ac = AudacityController()
|
||||
self.ac: Optional[AudacityController] = AudacityController()
|
||||
except ApplicationError as e:
|
||||
self.ac = None
|
||||
show_warning(self.musicmuster, "Audacity error", str(e))
|
||||
@ -272,9 +324,15 @@ class PlaylistTab(QTableView):
|
||||
# Stretch last column *after* setting column widths which is
|
||||
# *much* faster
|
||||
h_header = self.horizontalHeader()
|
||||
if isinstance(h_header, QHeaderView):
|
||||
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()
|
||||
@ -370,6 +428,16 @@ class PlaylistTab(QTableView):
|
||||
self.reset()
|
||||
super().mouseReleaseEvent(event)
|
||||
|
||||
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)
|
||||
|
||||
def selectionChanged(
|
||||
self, selected: QItemSelection, deselected: QItemSelection
|
||||
) -> None:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user