提问人:Radek Wysocki 提问时间:11/2/2023 最后编辑:Radek Wysocki 更新时间:11/2/2023 访问量:36
Pyside2/6 QColumnView 似乎坏了,相同的模型适用于 QTreeView
Pyside2/6 QColumnView seems broken, same model works on QTreeView
问:
我正在开发一个应用程序,它涉及显示实体的分层结构。这些实体存储在一个无法完整获取的大型数据库中,因此我正在实现延迟加载,这意味着只有在用户展开父项时才会查询实体。
我遇到的问题是,当我单击一个实体时,我可以看到它正在控制台中加载,但是在我单击另一个项目然后返回到原始项目之前,UI 不会更新。有趣的是,当我用 QTreeView 替换 QColumnView 时,一切都按预期工作。
我怀疑这可能是 PySide 或 Qt 中的一个错误。但是,我愿意接受我可能忽略某些东西的可能性。我尝试发出不同的信号来指示我已更新该区域,但我的尝试都没有成功。我的代码目前使用的是 PySide2,但我也用 PySide6 对其进行了测试(只需将导入和 exec_() 替换为 exec()),问题仍然存在。任何见解或建议将不胜感激。
--编辑--
我之所以以这种方式实现加载,而不是 DirPathModel 处理它的方式,是因为我特别想只加载我单击的实体,而不是加载所有可见的实体。
--编辑结束--
我的代码:
# !/usr/bin/env python
# coding=utf-8
import logging
import time
from dataclasses import dataclass
from typing import Union, Any, List
import PySide2.QtCore
from PySide2.QtCore import QModelIndex, Qt
from PySide2.QtGui import QStandardItemModel, QStandardItem
from PySide2.QtWidgets import QColumnView, QApplication
class DatabaseConnector:
"""This queries database for projects and entities"""
def get_root_data(self) -> List[dict]:
time.sleep(1) # Emulate query to server, it takes time
return [
{
"id": "1",
"type": "Project",
"name": "Some project1"
},
{
"id": "2",
"type": "Project",
"name": "Some project2"
},
{
"id": "3",
"type": "Project",
"name": "Some project3"
},
]
def expand(self, f_id: str, f_type: str) -> List[dict]:
time.sleep(1) # Emulate query to server, it takes time
# Normally this would not return get_root_data, but for simplicity of example it does.
return self.get_root_data()
@dataclass
class Entity:
id: str
type: str
class BaseModel(QStandardItemModel):
"""Base model for entity selector"""
def __init__(self) -> None:
super().__init__()
self.data_provider = DatabaseConnector()
root_data = self.data_provider.get_root_data()
for data in root_data:
project_item = self._create_child(data)
self.invisibleRootItem().appendRow(project_item)
def _create_child(self, data) -> QStandardItem:
"""Creates a child item with a "Loading..." child."""
entity = Entity(data['id'], data['type'])
child_item = QStandardItem()
child_item.setText(data['name'])
child_item.setData(entity, Qt.ItemDataRole.UserRole)
# placeholder child because if child_item is not empty a call to data() will be made
loading_child = QStandardItem()
loading_child.setText("Loading...")
child_item.appendRow(loading_child)
return child_item
def data(self, index: Union[PySide2.QtCore.QModelIndex, PySide2.QtCore.QPersistentModelIndex],
role: int = ...) -> Any:
data = super().data(index, role)
# We catch the placeholder "Loading..." child, which means we have to
# populate the parent entity.
if role == Qt.ItemDataRole.DisplayRole and data == "Loading...":
parent_index = index.parent()
if not parent_index.isValid():
return data
logging.warning("Filling")
self._fill_parent(parent_index)
return data
def _fill_parent(self, parent: QModelIndex) -> None:
"""Fills the parent entity with children"""
parent_item = self.itemFromIndex(parent)
entity: Entity = parent_item.data(Qt.ItemDataRole.UserRole)
# Remove the placeholder
while parent_item.rowCount() > 0:
parent_item.removeRow(0)
# Ask the server for children
children_data = self.data_provider.expand(entity.id, entity.type)
print("Data from server", children_data)
# Insert the children
for child in children_data:
print(f"Appending {child['name']}")
child_item = self._create_child(child)
parent_item.appendRow(child_item)
if __name__ == '__main__':
app = QApplication()
model = BaseModel()
entity_selector = QColumnView()
entity_selector.setModel(model)
entity_selector.resize(800, 200)
entity_selector.show()
app.exec_()
答: 暂无答案
评论
data()
QTimer.singleShot(0, lambda: self._fill_parent(parent_index))
QStandardItemModel::data()
QStandardItemModel::data()