Compare commits
8 Commits
bb14b34c2e
...
5d19d1ed9f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5d19d1ed9f | ||
|
|
93d780f75a | ||
|
|
b75dc4256a | ||
|
|
d0645a1768 | ||
|
|
0690a66806 | ||
|
|
07669043eb | ||
|
|
d579eb81b4 | ||
|
|
cbdcd5f4fc |
@ -51,7 +51,7 @@ class MyTableWidget(QTableWidget):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setItemDelegate(EscapeDelegate(self))
|
||||
self.setEditTriggers(QAbstractItemView.EditTrigger.DoubleClicked)
|
||||
# self.setEditTriggers(QAbstractItemView.EditTrigger.DoubleClicked)
|
||||
|
||||
|
||||
class MainWindow(QMainWindow):
|
||||
|
||||
91
InterceptEscapeWhenEditingTableCellInView.py
Executable file
91
InterceptEscapeWhenEditingTableCellInView.py
Executable file
@ -0,0 +1,91 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from PyQt6.QtCore import Qt, QEvent, QObject, QVariant, QAbstractTableModel
|
||||
from PyQt6.QtWidgets import (
|
||||
QApplication,
|
||||
QMainWindow,
|
||||
QMessageBox,
|
||||
QPlainTextEdit,
|
||||
QStyledItemDelegate,
|
||||
QTableView,
|
||||
)
|
||||
|
||||
from PyQt6.QtGui import QKeyEvent
|
||||
|
||||
from typing import cast
|
||||
|
||||
|
||||
class EscapeDelegate(QStyledItemDelegate):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
|
||||
def createEditor(self, parent, option, index):
|
||||
return QPlainTextEdit(parent)
|
||||
|
||||
def eventFilter(self, editor: QObject, event: QEvent):
|
||||
"""By default, QPlainTextEdit doesn't handle enter or return"""
|
||||
|
||||
if event.type() == QEvent.Type.KeyPress:
|
||||
key_event = cast(QKeyEvent, event)
|
||||
print(key_event.key())
|
||||
if key_event.key() == Qt.Key.Key_Return:
|
||||
if key_event.modifiers() == (Qt.KeyboardModifier.ControlModifier):
|
||||
print("save data")
|
||||
self.commitData.emit(editor)
|
||||
self.closeEditor.emit(editor)
|
||||
return True
|
||||
elif key_event.key() == Qt.Key.Key_Escape:
|
||||
discard_edits = QMessageBox.question(
|
||||
self.parent(), "Abandon edit", "Discard changes?"
|
||||
)
|
||||
if discard_edits == QMessageBox.StandardButton.Yes:
|
||||
print("abandon edit")
|
||||
self.closeEditor.emit(editor)
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class MyTableWidget(QTableView):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setItemDelegate(EscapeDelegate(self))
|
||||
self.setModel(MyModel())
|
||||
|
||||
|
||||
class MyModel(QAbstractTableModel):
|
||||
|
||||
def columnCount(self, index):
|
||||
return 2
|
||||
|
||||
def rowCount(self, index):
|
||||
return 2
|
||||
|
||||
def data(self, index, role):
|
||||
if not index.isValid() or not (0 <= index.row() < 2):
|
||||
return QVariant()
|
||||
|
||||
row = index.row()
|
||||
column = index.column()
|
||||
if role == Qt.ItemDataRole.DisplayRole:
|
||||
return QVariant(f"Row {row}, Col {column}")
|
||||
return QVariant()
|
||||
|
||||
def flags(self, index):
|
||||
return Qt.ItemFlag.ItemIsEnabled | Qt.ItemFlag.ItemIsSelectable | Qt.ItemFlag.ItemIsEditable
|
||||
|
||||
|
||||
class MainWindow(QMainWindow):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.table_widget = MyTableWidget(self)
|
||||
self.setCentralWidget(self.table_widget)
|
||||
|
||||
self.table_widget.resizeColumnsToContents()
|
||||
self.table_widget.resizeRowsToContents()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = QApplication([])
|
||||
window = MainWindow()
|
||||
window.show()
|
||||
app.exec()
|
||||
@ -246,6 +246,7 @@ class MusicMusterSignals(QObject):
|
||||
|
||||
set_next_track_signal = pyqtSignal(int, int)
|
||||
span_cells_signal = pyqtSignal(int, int, int, int)
|
||||
enable_escape_signal = pyqtSignal(bool)
|
||||
|
||||
|
||||
class PlaylistTrack:
|
||||
@ -328,7 +329,7 @@ class PlaylistTrack:
|
||||
|
||||
class Window(QMainWindow, Ui_MainWindow):
|
||||
def __init__(self, parent=None, *args, **kwargs) -> None:
|
||||
super().__init__(*args, **kwargs)
|
||||
super().__init__(parent)
|
||||
self.setupUi(self)
|
||||
|
||||
self.timer10: QTimer = QTimer()
|
||||
@ -691,6 +692,8 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
self.tabBar.tabMoved.connect(self.move_tab)
|
||||
self.txtSearch.returnPressed.connect(self.search_playlist_return)
|
||||
|
||||
self.signals.enable_escape_signal.connect(self.enable_escape)
|
||||
|
||||
self.timer10.timeout.connect(self.tick_10ms)
|
||||
self.timer500.timeout.connect(self.tick_500ms)
|
||||
self.timer1000.timeout.connect(self.tick_1000ms)
|
||||
@ -725,7 +728,6 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
|
||||
playlist_tab = PlaylistTab(
|
||||
musicmuster=self,
|
||||
session=session,
|
||||
playlist_id=playlist.id,
|
||||
signals=self.signals,
|
||||
)
|
||||
@ -812,6 +814,16 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
else:
|
||||
self.music.set_volume(Config.VOLUME_VLC_DEFAULT, set_default=False)
|
||||
|
||||
def enable_escape(self, enabled: bool) -> None:
|
||||
"""
|
||||
Manage signal to enable/disable handling ESC character.
|
||||
|
||||
Needed because we want to use ESC when editing playlist in place,
|
||||
so we need to disable it here while editing.
|
||||
"""
|
||||
|
||||
self.action_Clear_selection.setEnabled(enabled)
|
||||
|
||||
def enable_play_next_controls(self) -> None:
|
||||
"""
|
||||
Enable "play next" keyboard controls
|
||||
|
||||
@ -91,11 +91,6 @@ class PlaylistModel(QAbstractTableModel):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.playlist_rows: dict[int, PlaylistRowData] = {}
|
||||
# self.current_row = None
|
||||
# self.next_row = None
|
||||
self.previous_row = None
|
||||
self.current_row = 2
|
||||
self.next_row = 3
|
||||
|
||||
self.refresh_data()
|
||||
|
||||
@ -148,7 +143,7 @@ class PlaylistModel(QAbstractTableModel):
|
||||
elif role == Qt.ItemDataRole.DecorationRole:
|
||||
pass
|
||||
elif role == Qt.ItemDataRole.EditRole:
|
||||
pass
|
||||
return self.edit_role(row, column, prd)
|
||||
elif role == Qt.ItemDataRole.ToolTipRole:
|
||||
pass
|
||||
elif role == Qt.ItemDataRole.StatusTipRole:
|
||||
@ -173,6 +168,20 @@ class PlaylistModel(QAbstractTableModel):
|
||||
# Fall through to no-op
|
||||
return QVariant()
|
||||
|
||||
def edit_role(self, row: int, column: int, prd: PlaylistRowData) -> QVariant:
|
||||
"""
|
||||
Return text for editing
|
||||
"""
|
||||
|
||||
if column == Col.TITLE.value:
|
||||
return QVariant(prd.title)
|
||||
if column == Col.ARTIST.value:
|
||||
return QVariant(prd.artist)
|
||||
if column == Col.NOTE.value:
|
||||
return QVariant(prd.note)
|
||||
|
||||
return QVariant()
|
||||
|
||||
def display_role(self, row: int, column: int, prd: PlaylistRowData) -> QVariant:
|
||||
"""
|
||||
Return text for display
|
||||
@ -214,6 +223,34 @@ class PlaylistModel(QAbstractTableModel):
|
||||
|
||||
return QVariant()
|
||||
|
||||
def edit_role(self, row: int, column: int, prd: PlaylistRowData) -> QVariant:
|
||||
"""
|
||||
Return text for editing
|
||||
"""
|
||||
|
||||
if column == Col.TITLE.value:
|
||||
return QVariant(prd.title)
|
||||
if column == Col.ARTIST.value:
|
||||
return QVariant(prd.artist)
|
||||
if column == Col.NOTE.value:
|
||||
return QVariant(prd.note)
|
||||
|
||||
return QVariant()
|
||||
|
||||
def flags(self, index: QModelIndex) -> Qt.ItemFlag:
|
||||
"""
|
||||
Standard model flags
|
||||
"""
|
||||
|
||||
if not index.isValid():
|
||||
return Qt.ItemFlag.ItemIsEnabled
|
||||
|
||||
default = Qt.ItemFlag.ItemIsEnabled | Qt.ItemFlag.ItemIsSelectable
|
||||
if index.column() in [Col.TITLE.value, Col.ARTIST.value, Col.NOTE.value]:
|
||||
return default | Qt.ItemFlag.ItemIsEditable
|
||||
|
||||
return default
|
||||
|
||||
def headerData(
|
||||
self,
|
||||
section: int,
|
||||
@ -266,3 +303,13 @@ class PlaylistModel(QAbstractTableModel):
|
||||
"""Standard function for view"""
|
||||
|
||||
return len(self.playlist_rows)
|
||||
|
||||
def setData(
|
||||
self, index: QModelIndex, value: QVariant, role: int = Qt.ItemDataRole.EditRole
|
||||
) -> bool:
|
||||
"""
|
||||
Update model with edited data
|
||||
"""
|
||||
|
||||
print(f"setData({index.row()=}, {index.column()=}, {value=}, {role=})")
|
||||
return True
|
||||
|
||||
1925
app/playlists.py
1925
app/playlists.py
File diff suppressed because it is too large
Load Diff
2584
app/playlists_v3.py
2584
app/playlists_v3.py
File diff suppressed because it is too large
Load Diff
BIN
archive/todo/.DS_Store
vendored
Normal file
BIN
archive/todo/.DS_Store
vendored
Normal file
Binary file not shown.
1
archive/todo/data.db
Normal file
1
archive/todo/data.db
Normal file
@ -0,0 +1 @@
|
||||
[[false, "My first todo"], [true, "My second todo"], [true, "Another todo"], [false, "as"]]
|
||||
71
archive/todo/mainwindow.ui
Normal file
71
archive/todo/mainwindow.ui
Normal file
@ -0,0 +1,71 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MainWindow</class>
|
||||
<widget class="QMainWindow" name="MainWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>275</width>
|
||||
<height>314</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Todo</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QListView" name="todoView">
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::SingleSelection</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="widget" native="true">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="deleteButton">
|
||||
<property name="text">
|
||||
<string>Delete</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="completeButton">
|
||||
<property name="text">
|
||||
<string>Complete</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="todoEdit"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="addButton">
|
||||
<property name="text">
|
||||
<string>Add Todo</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QMenuBar" name="menubar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>275</width>
|
||||
<height>22</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar"/>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
BIN
archive/todo/tick.png
Executable file
BIN
archive/todo/tick.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 634 B |
106
archive/todo/todo.py
Normal file
106
archive/todo/todo.py
Normal file
@ -0,0 +1,106 @@
|
||||
import sys
|
||||
import datetime
|
||||
import json
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets, uic
|
||||
from PyQt5.QtCore import Qt
|
||||
|
||||
|
||||
qt_creator_file = "mainwindow.ui"
|
||||
Ui_MainWindow, QtBaseClass = uic.loadUiType(qt_creator_file)
|
||||
tick = QtGui.QImage('tick.png')
|
||||
|
||||
|
||||
class TodoModel(QtCore.QAbstractListModel):
|
||||
def __init__(self, *args, todos=None, **kwargs):
|
||||
super(TodoModel, self).__init__(*args, **kwargs)
|
||||
self.todos = todos or []
|
||||
|
||||
def data(self, index, role):
|
||||
if role == Qt.DisplayRole:
|
||||
_, text = self.todos[index.row()]
|
||||
return text
|
||||
|
||||
if role == Qt.DecorationRole:
|
||||
status, _ = self.todos[index.row()]
|
||||
if status:
|
||||
return tick
|
||||
|
||||
def rowCount(self, index):
|
||||
return len(self.todos)
|
||||
|
||||
def flags(self, index):
|
||||
print(datetime.datetime.now().time().strftime("%H:%M:%S"))
|
||||
return super().flags(index)
|
||||
|
||||
|
||||
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
|
||||
def __init__(self):
|
||||
QtWidgets.QMainWindow.__init__(self)
|
||||
Ui_MainWindow.__init__(self)
|
||||
self.setupUi(self)
|
||||
self.model = TodoModel()
|
||||
self.load()
|
||||
self.todoView.setModel(self.model)
|
||||
self.addButton.pressed.connect(self.add)
|
||||
self.deleteButton.pressed.connect(self.delete)
|
||||
self.completeButton.pressed.connect(self.complete)
|
||||
|
||||
def add(self):
|
||||
"""
|
||||
Add an item to our todo list, getting the text from the QLineEdit .todoEdit
|
||||
and then clearing it.
|
||||
"""
|
||||
text = self.todoEdit.text()
|
||||
if text: # Don't add empty strings.
|
||||
# Access the list via the model.
|
||||
self.model.todos.append((False, text))
|
||||
# Trigger refresh.
|
||||
self.model.layoutChanged.emit()
|
||||
# Empty the input
|
||||
self.todoEdit.setText("")
|
||||
self.save()
|
||||
|
||||
def delete(self):
|
||||
indexes = self.todoView.selectedIndexes()
|
||||
if indexes:
|
||||
# Indexes is a list of a single item in single-select mode.
|
||||
index = indexes[0]
|
||||
# Remove the item and refresh.
|
||||
del self.model.todos[index.row()]
|
||||
self.model.layoutChanged.emit()
|
||||
# Clear the selection (as it is no longer valid).
|
||||
self.todoView.clearSelection()
|
||||
self.save()
|
||||
|
||||
def complete(self):
|
||||
indexes = self.todoView.selectedIndexes()
|
||||
if indexes:
|
||||
index = indexes[0]
|
||||
row = index.row()
|
||||
status, text = self.model.todos[row]
|
||||
self.model.todos[row] = (True, text)
|
||||
# .dataChanged takes top-left and bottom right, which are equal
|
||||
# for a single selection.
|
||||
self.model.dataChanged.emit(index, index)
|
||||
# Clear the selection (as it is no longer valid).
|
||||
self.todoView.clearSelection()
|
||||
self.save()
|
||||
|
||||
def load(self):
|
||||
try:
|
||||
with open('data.db', 'r') as f:
|
||||
self.model.todos = json.load(f)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def save(self):
|
||||
with open('data.db', 'w') as f:
|
||||
data = json.dump(self.model.todos, f)
|
||||
|
||||
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
window = MainWindow()
|
||||
window.show()
|
||||
app.exec_()
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user