I have created a custom table model Python class
, subclassing the QAbstractTableModel
and when I try to set the instance of this class, model, to the model
property of the TableView
, the whole app crashes. There are no error debug info in the terminal about what is causing the crash. I am using QML with version Qt 6.4
and PySide6
.
code:
main.py:
# This Python file uses the following encoding: utf-8
import sys
import os
from PySide6.QtCore import QUrl, QObject, Slot, Qt, QAbstractTableModel
from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine, QJSValue, qmlRegisterType
import create_table_model_E1
class Comm(QObject):
'''
Object - slot-owner and signal acceptor
'''
def __init__(self):
super().__init__()
# Signal reciever for signal rowColData from qml that contains header data for table
@Slot(QJSValue, QJSValue)
def handle_row_col_data(self, row_names: QJSValue, col_names: QJSValue):
row_names = row_names.toVariant()
col_names = col_names.toVariant()
print("Signal received from QML - Row Names:", row_names)
print("Signal received from QML - Column Names:", col_names)
print("Creating Table Model..")
model = create_table_model_E1.create_model(row_names, col_names, False)
if isinstance(model, QAbstractTableModel):
tableView.setProperty("model", model)
# pass
## tried, importing it in qml file doesnt work
# # Register the CustomTableModel with QML
# sys.path.append('create_table_model_E1.py')
# qmlRegisterType(create_table_model_E1.CustomTableModel, "myCustomTableModel", 1, 0,
# "MyCustomTableModel")
##
if __name__ == '__main__':
# Create a QApplication instance
app = QGuiApplication(sys.argv)
# Get the absolute path to the QML file
qml_file = os.path.abspath('content/App.qml')
# Reciever class
com = Comm()
# Create a QQmlApplicationEngine instance
engine = QQmlApplicationEngine()
# Load the main QML file
engine.load(QUrl.fromLocalFile(qml_file))
qml_obj = engine.rootObjects()[0]
# find the object that emits the signal containing the data
rowColData_comp = qml_obj.findChild(QObject, "data_to_table")
rowColData_comp.rowColData.connect(com.handle_row_col_data,
type=Qt.ConnectionType.QueuedConnection)
# find the tableview component
tableView = qml_obj.findChild(QObject, "TableView")
# If the rootObjects() method of the QQmlApplicationEngine
# instance returns an empty list,
# it means the QML file could not be loaded, so exit the
# application with a status code of -1
if not engine.rootObjects():
sys.exit(-1)
# Start the main event loop of the application by calling app.exec()
sys.exit(app.exec())
create_table_model_E1.py:
class CustomTableModel(QAbstractTableModel):
dataChanged = Signal(QModelIndex, QModelIndex, list)
def __init__(self, data, headers, parent=None) -> None:
super(CustomTableModel, self).__init__(parent)
self._data = data
self._headers = headers
def rowCount(self, parent=None) -> int:
# Return the number of rows in the table
return len(self._data)
def columnCount(self, parent=None) -> int:
# Return the number of columns in the table
return len(self._data[0])
def data(self, index: Union[QModelIndex, QPersistentModelIndex], role=Qt.DisplayRole) -> Any:
# Return the data for a specific index and role
if not index.isValid():
return None
row = index.row()
col = index.column()
if role == Qt.DisplayRole:
# Return the display data for the cell
return self._data[row][col]
def headerData(self, section, orientation: Qt.Orientation, role=Qt.DisplayRole) -> Any:
# Return the header data for a specific section and role
if role == Qt.DisplayRole:
if orientation == Qt.Horizontal:
# Return the horizontal header data
return self._headers[section]
elif orientation == Qt.Vertical:
# Return the vertical header data
return str(section + 1)
# ... editable model methods ...
def setData(self, index: Union[QModelIndex, QPersistentModelIndex], value: Any, role: int = Qt.EditRole) -> bool: # noqa
if role == Qt.EditRole:
row = index.row()
column = index.column()
if 0 <= row < self.rowCount() and 0 <= column < self.columnCount():
# Update the data in the internal data structure
self._data[row][column] = value
# Emit dataChanged signal to notify the view
self.dataChanged.emit(index, index, [role])
return True
return False
def flags(self, index: Union[QModelIndex, QPersistentModelIndex]) -> Qt.ItemFlag:
default_flags = super().flags(index)
if index.isValid():
# Set the item flags to be editable
return default_flags | Qt.ItemIsEditable
return default_flags
# ... other methods ...
def insertRows(self, row, count, parent=QModelIndex()) -> bool:
self.beginInsertRows(parent, row, row + count - 1)
for _ in range(count):
empty_row = [' '] * self.columnCount()
self._data.insert(row, empty_row)
self.endInsertRows()
return True
def insertColumns(self, column, count, parent=QModelIndex()) -> bool:
self.beginInsertColumns(parent, column, column + count - 1)
for _ in range(count):
for row in self._data:
row.insert(column, ' ')
self._headers.insert(column, ' ')
self.endInsertColumns()
return True
def removeRows(self, row, count, parent=QModelIndex()) -> bool:
self.beginRemoveRows(parent, row, row + count - 1)
for _ in range(count):
self._data.pop(row)
self.endRemoveRows()
return True
def removeColumns(self, column, count, parent=QModelIndex()) -> bool:
self.beginRemoveColumns(parent, column, column + count - 1)
for row in self._data:
for _ in range(count):
row.pop(column)
self._headers.pop(column)
self.endRemoveColumns()
return True
def getAllData(self) -> list:
"""
Return all data in the model.
"""
return self._data
def create_model(rowsn: list, colsn: list, isEl3: bool = True) -> QAbstractTableModel:
rownames = ['names']
colnames = ['names']
for col in colsn:
colnames.append(col)
for row in rowsn:
rownames.append(row)
rownames.append('Weights')
if isEl3:
rownames.append('Indifference(q)')
rownames.append('Preference(p)')
rownames.append('Veto')
data = []
temp = []
for rid, row in enumerate(rownames):
for id, col in enumerate(colnames):
if rid == 0:
temp.append(col)
else:
if id == 0:
temp.append(row)
else:
temp.append(' ')
data.append(copy(temp))
temp.clear()
model = CustomTableModel(data, data[0])
return model
Table.qml:
import QtQuick
import QtQuick.Controls 2.15
// import myCustomTableModel 1.0 doesnt work
Item {
id: root
width: 400
height: 200
property var colNames: ["names", "cr1", "cr2", "cr3"]
property var tableSize: [view.rowHeightProvider, view.columnWidthProvider]
TableView {
id: view
objectName: "TableView"
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
boundsBehavior: Flickable.StopAtBounds
focus: true
interactive: true
anchors.topMargin: 0
rowSpacing: -1
columnSpacing: -1
clip: false
property bool tabpressed: false
property bool returnpressed: false
property bool uppressed: false
property bool downpressed: false
property bool leftpressed: false
property bool rightpressed: false
// user-specific table model
property var userTableModel: null
rowHeightProvider: function (index) {
return 30
}
columnWidthProvider: function (index) {
return 100
}
selectionModel: ItemSelectionModel {
id: itemSelectionModel
model: view.model
}
model: TableModelCustom { // dummy model
id: model
}
delegate: Table_customDelegate_FROM_UPWORK {
id: viewdelegate
width: 100
height: 30
}
onUserTableModelChanged: {
console.log("[Table_custom.qml]: USER TABLE MODEL CHANGED")
if (view.userTableModel != null) {
view.model = view.userTableModel
console.log("NEW MODEL: " + view.userTableModel)
// SOMEWHERE HERE IT CRASHES
}
}
}
HorizontalHeaderView {
id: horizontalheader
x: 0
y: 0
width: view.width
height: 30
boundsBehavior: Flickable.StopAtBounds
interactive: true
clip: true
model: colNames
delegate: Item {
id: wrapper
implicitWidth: 100
implicitHeight: 30
Rectangle {
id: background
color: "#dbdbdb"
border.color: "#000000"
border.width: 1
anchors.fill: parent
Rectangle {
id: rectangle
x: 1
width: 1
color: index === 0 ? "#222222" : "#00ffffff"
border.width: 0
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.bottomMargin: 0
anchors.topMargin: 0
}
}
Text {
id: text1
color: "#3b3b3b"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
anchors.rightMargin: 2
anchors.bottomMargin: 2
anchors.topMargin: 2
text: horizontalheader.model[row]
anchors.fill: parent
}
}
syncView: view
}
}
I tried creating the model, returning it to main.py
, and then passing it to TableView
using setProperty
, directly, or by setting the property var userTableModel
, and then setting this to the model
property of TableView
, but when the model cahnges, the whole app crashes. All these get done on runtime. I checked the tableView
varible in main.py
, and it is not a widget type(QTableView
), it is QuickItemType(tableView.isQuickItemType()
returns True
) and thats all I could find about this. What am I missing? Why on changing the model from the existing to the newly created crashes the app?
In summary: I want to change the model of the TableView,on runtime, and when i do it, the app crashes.
Sorry for the long code blocks.