TableView struggles
-
I have spent a couple of afternoons, can't get it to work; I am just going in circles.
Below, I will include both Python and QML sources of a minimal kind-of-working program.
There are two features I can't figure out how to implement and I would very much appreciate some assistance.
Desired features:
- Keyboard navigation
- Editing of cell values
As follows:
- When I click on a cell, I would like it to be highlighted in some way so I know where I am at.
- After that, I would like to be able to use the tab key or the arrow keys to navigate the table.
- When I double-click on a cell or press <enter> while highlighted, I would like for that cell to enter edit mode; and, most importantly (which is the part I can't get to work), when I leave edit mode, I would like the new value to be reflected in the table.
Here is the Python code:
import os import sys import json from pathlib import Path from PySide6.QtGui import QGuiApplication from PySide6.QtQml import QQmlApplicationEngine from PySide6.QtCore import ( QAbstractTableModel, QModelIndex, QObject, Qt, Property, Signal, Slot ) script_dir = Path( os.path.dirname(os.path.abspath(__file__)) ) # --- MyTableModel --- # A custom model for use with QML's TableView. # It provides methods for reading, writing, and modifying table data. class MyTableModel(QAbstractTableModel): def __init__(self, header=None, data=None, parent=None): super().__init__(parent) self._header = header or [] self._data = data or [] def rowCount(self, parent=QModelIndex()): return len(self._data) def columnCount(self, parent=QModelIndex()): if self._data: return len(self._data[0]) return 0 def data(self, index, role=Qt.DisplayRole): if not index.isValid(): return None row = index.row() col = index.column() if row >= len(self._data) or col >= len(self._data[0]): return None if role == Qt.DisplayRole: return str(self._data[row][col]) @Slot(int, Qt.Orientation, result="QVariant") def headerData(self, section, orientation, role=Qt.DisplayRole): if role == Qt.DisplayRole: if orientation == Qt.Horizontal: return self._header[section] else: return str(section) # required for editable models def setData(self, index, value, role=Qt.EditRole): if role == Qt.EditRole: if index.isValid() and 0 <= index.row() < self.rowCount() and 0 <= index.column() < self.columnCount(): row = index.row() col = index.column() self._data[row][col] = value self.dataChanged.emit(index, index, [role]) return True return False # required for editable models, to inform the view which roles are editable def flags(self, index): if not index.isValid(): return Qt.NoItemFlags return Qt.ItemIsEditable | Qt.ItemIsSelectable | Qt.ItemIsEnabled # = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = # --- MyTableManager --- # A QObject that provides the interface between QML and MyTableModel. class MyTableManager(QObject): dataChanged = Signal() def __init__(self, model): super().__init__() self._model = model self._selected_row = -1 @Property(QObject, constant=True) def model(self): return self._model @Slot(str) def loadData(self, filepath): self._model.loadData(filepath) @Slot(int, int, str) def updateValue(self, row, col, value): index = self._model.index(row, col, QModelIndex()) self._model.setData(index, value) if __name__ == "__main__": app = QGuiApplication(sys.argv) engine = QQmlApplicationEngine() items = [ [ "1", "Alice" , "30"], [ "2", "Bob" , "25"], [ "3", "Charlie", "35"] ] my_table_model = MyTableModel(data=items) manager = MyTableManager(my_table_model) engine.rootContext().setContextProperty("tableManager", manager) engine.rootContext().setContextProperty("tableModel", my_table_model) qml_file = Path(__file__).parent / "gen.qml" engine.load(qml_file) if not engine.rootObjects(): sys.exit(-1) sys.exit(app.exec())
Here is the QML source:
import QtQuick import QtQuick.Controls.Basic Window { visible: true width: 320 height: 180 title: qsTr("Hello World") color: '#222222' TableView { id: tableView columnWidthProvider: function (column) { return 100; } rowHeightProvider: function (column) { return 40; } anchors.fill: parent model: tableModel delegate: Rectangle { implicitWidth: 100 implicitHeight: 40 color: (index % 2 === 0) ? "#FFFFFF" : "#F9F9F9" border.width: 1 border.color: "lightgray" Text { text: display anchors.fill: parent anchors.margins: 5 verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter } TableView.editDelegate: TextField { anchors.fill: parent text: display horizontalAlignment: TextInput.AlignHCenter verticalAlignment: TextInput.AlignVCenter TableView.onCommit: { tableManager.updateValue(row, column, text) } } } } }
-
For keyboard navigation, the documentation says to set the
keyNavigationEnabled
property to true. This needs at least Qt 6.4. It also says you need to assign anItemSelectionModel
toselectionModel
.My recollection is that item selection models and their use are not particularly well documented in QML. See this forum post that might help: https://forum.qt.io/topic/133628/how-to-use-itemselectionmodel-with-tableview. It links to a Stack Overflow question that might also be useful.