Issue
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.
Solution
How about registering the model class as type and just use it in the qml file?
Call
qmlRegisterType(CustomTableModel, 'CustomTableModel', 1, 0, 'CustomTableModel')
in your main.py.
Then you can import this type in the Table.qml file and use the model:
import CustomTableModel
No need for the additional property userTableModel. If you want to fill the data on the Python side, register an instance of the model as singleton with qmlRegisterSingletonInstance.
Answered By - Jürgen Lutz
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.