Я создал пользовательскую табличную модель Python class
, создав подкласс QAbstractTableModel
, и когда я пытаюсь установить экземпляр этого класса, модели, в свойство model
TableView
, все приложение падает. В терминале нет информации об отладке ошибок о том, что вызывает сбой. Я использую QML версии Qt 6.4
и PySide6
.
код:
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
Таблица.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.info("[Table_custom.qml]: USER TABLE MODEL CHANGED")
if (view.userTableModel != null) {
view.model = view.userTableModel
console.info("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
}
}
Я попытался создать модель, вернуть ее в main.py
, а затем передать в TableView
, используя setProperty
, напрямую или установив property var userTableModel
, а затем установив это в свойство model
TableView
, но когда модель изменяется, все приложение падает. Все это делается во время выполнения. Я проверил переменную tableView
в main.py
, и это не тип виджета (QTableView
), это QuickItemType (tableView.isQuickItemType()
возвращает True
), и это все, что я мог найти об этом. Что мне не хватает? Почему при смене модели с существующей на вновь созданную приложение вылетает?
Подводя итог: я хочу изменить модель TableView во время выполнения, и когда я это делаю, приложение вылетает.
Извините за длинные блоки кода.
Как насчет того, чтобы зарегистрировать класс модели как тип и просто использовать его в файле qml?
Вызов
qmlRegisterType(CustomTableModel, 'CustomTableModel', 1, 0, 'CustomTableModel')
в вашем main.py.
Затем вы можете импортировать этот тип в файл Table.qml и использовать модель:
import CustomTableModel
Нет необходимости в дополнительном свойстве userTableModel. Если вы хотите заполнить данные на стороне Python, зарегистрируйте экземпляр модели как синглтон с помощью qmlRegisterSingletonInstance.
Кроме того, мне не просто нужно заполнить данные на стороне Python, я также хочу изменить свойство модели, которое установлено на фиктивную модель, и изменить его на модель, возвращаемую из пользовательского подкласса в Python.
Я не могу сказать, почему Qt Creator не знает ваш тип. Иногда помогает сброс модели кода QML, потому что кэширование типов немного неуклюже. Какой тип ошибки вы получаете?
Вы можете добавить свойство модели в качестве псевдонима модели внутренней таблицы. Чем более последовательно менять модель (псевдоним свойства tableModel: view.model)
Спасибо за ответ. Из того, что вы сказали, у меня появилась еще одна идея: не изменять модель, назначая новую во время выполнения, а сначала устанавливая пустую модель из пользовательского класса модели, а затем просто обновляя данные. Кажется, что-то (pyside, движок qml или что-то еще) ненавидит такие изменения. Таким образом, установка пустой модели, а затем ее изменение вместо создания и повторного назначения модели помогает, но у меня такое ощущение, что это скорее обходной путь, чем правильное использование фреймворка.
Кроме того, делая то, что я описал в предыдущем комментарии, движок автоматически распознает тип, который я объявляю. Странные вещи, я знаю
Привет, я пробовал, но не знаю, почему это не сработало. Редактирование кода qml (импорт и использование) в создателе qt или студии дизайна не может распознать новый зарегистрированный тип, и, конечно, это приводит к невозможности даже запустить все приложение.