I have a source model source_model = TokenModel(QAbstractListModel)
, which I use with several views at once. It contains a list of tokens _tokens: list[TokenClass]
. I also want to use it in QTreeView. To do this, I create a TreeProxyModel(QAbstractProxyModel)
, but I can not achieve the display of the child elements of the tree structure. Is it possible to convert QAbstractListModel to QTreeView using a proxy model? How to do it?
import typing
from PyQt6 import QtWidgets
from PyQt6.QtCore import QAbstractListModel, QModelIndex, Qt, QVariant, QAbstractProxyModel
class TokenClass:
def __init__(self, token: str, accounts: list[str]):
self.token: str = token
self.accounts: list[str] = accounts # Список счетов.
class TokenModel(QAbstractListModel):
def __init__(self, token_class_list: list[TokenClass]):
super().__init__() # __init__() QAbstractListModel.
self._tokens: list[TokenClass] = token_class_list
def rowCount(self, parent: QModelIndex = ...) -> int:
return len(self._tokens)
def data(self, index: QModelIndex, role: int = ...) -> typing.Any:
if role == Qt.ItemDataRole.DisplayRole:
token_class: TokenClass = self._tokens[index.row()]
return QVariant(token_class.token)
else:
return QVariant()
def getTokenClass(self, row: int) -> TokenClass:
if 0 <= row < self.rowCount():
return self._tokens[row]
else:
raise ValueError("Invalid row value in getTokenClass() ({0})!".format(row))
class AccountItem:
def __init__(self, parent: QModelIndex, account: str):
self._account: str = account
self._parent: QModelIndex = parent
def parent(self) -> QModelIndex:
return self._parent
def data(self) -> str:
return self._account
class TreeProxyModel(QAbstractProxyModel):
def rowCount(self, parent: QModelIndex = ...) -> int:
if parent.isValid():
token: TokenClass = parent.internalPointer()
return len(token.accounts)
else:
return self.sourceModel().rowCount()
def columnCount(self, parent: QModelIndex = ...) -> int:
return 1
def data(self, index: QModelIndex, role: int = ...) -> typing.Any:
index_item: TokenClass | AccountItem = index.internalPointer()
if role == Qt.ItemDataRole.DisplayRole:
if type(index_item) == TokenClass:
return index_item.token
elif type(index_item) == AccountItem:
return index_item.data()
def index(self, row: int, column: int, parent: QModelIndex = ...) -> QModelIndex:
if parent.isValid():
token: TokenClass = parent.internalPointer()
account: str = token.accounts[row]
return self.createIndex(row, column, AccountItem(parent, account))
else:
token: TokenClass = self.sourceModel().getTokenClass(row)
return self.createIndex(row, column, token)
def parent(self, child: QModelIndex) -> QModelIndex:
if child.isValid():
data: TokenClass | AccountItem = child.internalPointer()
if type(data) == TokenClass:
return QModelIndex()
elif type(data) == AccountItem:
return data.parent()
else:
raise TypeError('Invalid element type: Type: {0}, Value: {1}!'.format(type(data), data))
else: # Если индекс child недействителен, то child - это счёт.
return QModelIndex()
def mapFromSource(self, sourceIndex: QModelIndex) -> QModelIndex:
return self.index(sourceIndex.row(), 0, QModelIndex())
def mapToSource(self, proxyIndex: QModelIndex) -> QModelIndex:
parent: QModelIndex = proxyIndex.parent()
if parent.isValid():
return QModelIndex()
else:
return self.sourceModel().index(proxyIndex.row(), 0, QModelIndex())
class Form(QtWidgets.QMainWindow):
def __init__(self, tokens: list[TokenClass]):
super().__init__() # __init__() QMainWindow.
self.centralwidget = QtWidgets.QWidget(self)
self.main_verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
self.treeView_tokens = QtWidgets.QTreeView(self.centralwidget)
self.main_verticalLayout.addWidget(self.treeView_tokens)
self.setCentralWidget(self.centralwidget)
source_model: TokenModel = TokenModel(tokens)
proxy_model: TreeProxyModel = TreeProxyModel()
proxy_model.setSourceModel(source_model)
self.treeView_tokens.setModel(proxy_model)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
token1: TokenClass = TokenClass('token1', ['account1', 'account2', 'account3'])
token2: TokenClass = TokenClass('token2', [])
token3: TokenClass = TokenClass('token3', ['account1'])
tokens: list[TokenClass] = [token1, token2, token3]
window = Form(tokens)
window.show()
sys.exit(app.exec())