QTreeView 和 QTableView 的 Qt 模型
Qt Model for both QTreeView and QTableView
我正在尝试创建一个可同时用于 QTableView 和 QTreeView 的模型。例如,我的数据是这样的:
ID
Location
Name
101
201
Apple
201
None
Kitchen
102
201
Banana
301
None
Cellar
302
301
Potatoes
202
302
Nail
所以每个条目都有一个位置,它本身就是模型中的一个条目。对于 QTableView,我想简单地显示所有条目,如上所示,而对于 QTreeView,我想像
- 201:厨房
- 101:苹果
- 102:香蕉
- 301:地窖
- 302:土豆
- 202:指甲
然而,我的问题是我不知道如何实现 QAbstractProxyModel.maptoSource() 或 mapfromSource(),因为我在 QTableView 中丢失了有关父项的信息。读 https://www.qtcentre.org/threads/26163-Map-table-to-tree-through-model-view-possible 看来,也许这根本就不可能。然而,QAbstractProxyModel 明确表示这是为了在两个视图中显示数据。任何人都可以指出我正确的方向或知道是否可以实施这样的模型吗?特别是在 Python 中,不幸的是我找不到任何例子。
我非常喜欢将未缩进的 TreeView 用作一种 TableView 的想法。不幸的是,我仍然无法创建模型。目前,仅显示顶部条目。
class MyModel(qtg.QStandardItemModel):
def __init__(
self,
engine
):
self.engine = engine
self.hierarchy_key = 'location_id'
self.column_names = ['id', 'location_id', 'name', 'quantity']
super().__init__(0, len(self.fields))
self.setHorizontalHeaderLabels(self.column_names)
self.root = self.invisibleRootItem()
self.build_model()
def build_model(self):
def add_children_to_tree(entries, parent_item):
for entry in entries:
items = []
for col in self.column_names:
text = getattr(entry, col)
item = qtg.QStandardItem(text)
items.append(qtg.QStandardItem(text))
parent_item.appendRow(items)
item = items[1] #the location_id item
parent_item.setChild(item.index().row(), item.index().column(), item)
with session_scope(self.engine) as session:
child_entries = (
session.query(self.entry_type)
.filter(
getattr(getattr(self.entry_type, self.hierarchy_key), "is_")(
entry.id
)
)
.all()
)
if child_entries:
add_children_to_tree(child_entries, item)
self.removeRows(0, self.rowCount())
with session_scope(self.engine) as session:
root_entries = session.query(self.entry_type).filter(getattr(getattr(self.entry_type, self.hierarchy_key), "is_")(None)).all()
if not isinstance(root_entries, list):
root_entries = [root_entries]
add_children_to_tree(root_entries, self.root)
这个想法是会话查询产生一个条目列表。每个条目都是数据库中的一条记录,具有属性“id”、“location_id”等。因此每个属性都是一个项目,项目列表在模型中创建一行。我无法弄清楚如何按照此处显示的方式使项目行成为另一行的子项:
我假设 setChild() 函数需要以不同的方式调用?
由于 python 明显缺乏示例,我将 post 此处使用我修改后的 simpletreemodel 版本,这最终对我有用。然后按照建议使用 QTreeView 而不是 QTableView,我也让 table 表现得像我想要的那样。总的来说,这会创建 MyItem,它是一个包含整行信息的项目,然后我使用递归将 children 添加到 parents,如果它们的值为 hierarchy_key (location_id ) 等于 parent.
的 ID
class MyItem(object):
def __init__(self, data, parent=None):
self.parentItem = parent
self.itemData = data
self.childItems = []
def appendChild(self, item):
self.childItems.append(item)
def child(self, row):
return self.childItems[row]
def childCount(self):
return len(self.childItems)
def columnCount(self):
return len(self.itemData)
def data(self, column=None):
try:
if column == None:
return [self.itemData[i] for i in range(self.columnCount())]
return self.itemData[column]
except IndexError:
return None
def parent(self):
return self.parentItem
def row(self):
if self.parentItem:
return self.parentItem.childItems.index(self)
return 0
class MyModel(QtCore.QAbstractItemModel):
def __init__(self, entry_type, engine, hierarchy_key, description_key, parent=None):
super(ORMModel, self).__init__(parent)
self.entry_type = entry_type
if isinstance(self.entry_type, str):
self.entry_type = getattr(ds, self.entry_type)
self.engine = engine
self.hierarchy_key = hierarchy_key
self.column_names = ['id', 'location_id', 'name', 'quantity']
self.rootItem = MyItem(self.column_names)
self.setHeaderData(0, Qt.Horizontal, self.rootItem)
self.initiateModel()
def root(self):
return self.rootItem
def columnCount(self, parent):
if parent.isValid():
return parent.internalPointer().columnCount()
else:
return self.rootItem.columnCount()
def data(self, index, role):
if not index.isValid():
return None
item = index.internalPointer()
if role == Qt.DisplayRole:
return item.data(index.column())
return None
def flags(self, index):
if not index.isValid():
return QtCore.Qt.NoItemFlags
return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
def headerData(self, section, orientation, role):
if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
return self.rootItem.data(section)
return None
def index(self, row, column, parent):
if not self.hasIndex(row, column, parent):
return QtCore.QModelIndex()
if not parent.isValid():
parentItem = self.rootItem
else:
parentItem = parent.internalPointer()
childItem = parentItem.child(row)
if childItem:
return self.createIndex(row, column, childItem)
else:
return QtCore.QModelIndex()
def parent(self, index):
if not index.isValid():
return QtCore.QModelIndex()
childItem = index.internalPointer()
parentItem = childItem.parent()
if parentItem == self.rootItem:
return QtCore.QModelIndex()
return self.createIndex(parentItem.row(), 0, parentItem)
def rowCount(self, parent):
if parent.column() > 0:
return 0
if not parent.isValid():
parentItem = self.rootItem
else:
parentItem = parent.internalPointer()
return parentItem.childCount()
def initiateModel(self):
def add_children_to_tree(entries, parent_item):
for entry in entries:
row = []
for field in self.fields.keys():
val = getattr(entry, field)
if isinstance(val, list):
text = "; ".join(map(str, val))
else:
text = str(val)
row.append(text)
item = ORMItem(row, parent_item)
parent_item.appendChild(item)
with session_scope(self.engine) as session:
child_entries = (
session.query(self.entry_type)
.filter(
getattr(
getattr(self.entry_type, self.hierarchy_key), "is_"
)(entry.id)
)
.all()
)
if child_entries:
add_children_to_tree(child_entries, item)
with session_scope(self.engine) as session:
root_entries = (
session.query(self.entry_type)
.filter(
getattr(getattr(self.entry_type, self.hierarchy_key), "is_")(None)
)
.all()
)
if not isinstance(root_entries, list):
root_entries = [root_entries]
add_children_to_tree(root_entries, self.rootItem)
我正在尝试创建一个可同时用于 QTableView 和 QTreeView 的模型。例如,我的数据是这样的:
ID | Location | Name |
---|---|---|
101 | 201 | Apple |
201 | None | Kitchen |
102 | 201 | Banana |
301 | None | Cellar |
302 | 301 | Potatoes |
202 | 302 | Nail |
所以每个条目都有一个位置,它本身就是模型中的一个条目。对于 QTableView,我想简单地显示所有条目,如上所示,而对于 QTreeView,我想像
- 201:厨房
- 101:苹果
- 102:香蕉
- 301:地窖
- 302:土豆
- 202:指甲
- 302:土豆
然而,我的问题是我不知道如何实现 QAbstractProxyModel.maptoSource() 或 mapfromSource(),因为我在 QTableView 中丢失了有关父项的信息。读 https://www.qtcentre.org/threads/26163-Map-table-to-tree-through-model-view-possible 看来,也许这根本就不可能。然而,QAbstractProxyModel 明确表示这是为了在两个视图中显示数据。任何人都可以指出我正确的方向或知道是否可以实施这样的模型吗?特别是在 Python 中,不幸的是我找不到任何例子。
我非常喜欢将未缩进的 TreeView 用作一种 TableView 的想法。不幸的是,我仍然无法创建模型。目前,仅显示顶部条目。
class MyModel(qtg.QStandardItemModel):
def __init__(
self,
engine
):
self.engine = engine
self.hierarchy_key = 'location_id'
self.column_names = ['id', 'location_id', 'name', 'quantity']
super().__init__(0, len(self.fields))
self.setHorizontalHeaderLabels(self.column_names)
self.root = self.invisibleRootItem()
self.build_model()
def build_model(self):
def add_children_to_tree(entries, parent_item):
for entry in entries:
items = []
for col in self.column_names:
text = getattr(entry, col)
item = qtg.QStandardItem(text)
items.append(qtg.QStandardItem(text))
parent_item.appendRow(items)
item = items[1] #the location_id item
parent_item.setChild(item.index().row(), item.index().column(), item)
with session_scope(self.engine) as session:
child_entries = (
session.query(self.entry_type)
.filter(
getattr(getattr(self.entry_type, self.hierarchy_key), "is_")(
entry.id
)
)
.all()
)
if child_entries:
add_children_to_tree(child_entries, item)
self.removeRows(0, self.rowCount())
with session_scope(self.engine) as session:
root_entries = session.query(self.entry_type).filter(getattr(getattr(self.entry_type, self.hierarchy_key), "is_")(None)).all()
if not isinstance(root_entries, list):
root_entries = [root_entries]
add_children_to_tree(root_entries, self.root)
这个想法是会话查询产生一个条目列表。每个条目都是数据库中的一条记录,具有属性“id”、“location_id”等。因此每个属性都是一个项目,项目列表在模型中创建一行。我无法弄清楚如何按照此处显示的方式使项目行成为另一行的子项:
由于 python 明显缺乏示例,我将 post 此处使用我修改后的 simpletreemodel 版本,这最终对我有用。然后按照建议使用 QTreeView 而不是 QTableView,我也让 table 表现得像我想要的那样。总的来说,这会创建 MyItem,它是一个包含整行信息的项目,然后我使用递归将 children 添加到 parents,如果它们的值为 hierarchy_key (location_id ) 等于 parent.
的 ID
class MyItem(object):
def __init__(self, data, parent=None):
self.parentItem = parent
self.itemData = data
self.childItems = []
def appendChild(self, item):
self.childItems.append(item)
def child(self, row):
return self.childItems[row]
def childCount(self):
return len(self.childItems)
def columnCount(self):
return len(self.itemData)
def data(self, column=None):
try:
if column == None:
return [self.itemData[i] for i in range(self.columnCount())]
return self.itemData[column]
except IndexError:
return None
def parent(self):
return self.parentItem
def row(self):
if self.parentItem:
return self.parentItem.childItems.index(self)
return 0
class MyModel(QtCore.QAbstractItemModel):
def __init__(self, entry_type, engine, hierarchy_key, description_key, parent=None):
super(ORMModel, self).__init__(parent)
self.entry_type = entry_type
if isinstance(self.entry_type, str):
self.entry_type = getattr(ds, self.entry_type)
self.engine = engine
self.hierarchy_key = hierarchy_key
self.column_names = ['id', 'location_id', 'name', 'quantity']
self.rootItem = MyItem(self.column_names)
self.setHeaderData(0, Qt.Horizontal, self.rootItem)
self.initiateModel()
def root(self):
return self.rootItem
def columnCount(self, parent):
if parent.isValid():
return parent.internalPointer().columnCount()
else:
return self.rootItem.columnCount()
def data(self, index, role):
if not index.isValid():
return None
item = index.internalPointer()
if role == Qt.DisplayRole:
return item.data(index.column())
return None
def flags(self, index):
if not index.isValid():
return QtCore.Qt.NoItemFlags
return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
def headerData(self, section, orientation, role):
if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
return self.rootItem.data(section)
return None
def index(self, row, column, parent):
if not self.hasIndex(row, column, parent):
return QtCore.QModelIndex()
if not parent.isValid():
parentItem = self.rootItem
else:
parentItem = parent.internalPointer()
childItem = parentItem.child(row)
if childItem:
return self.createIndex(row, column, childItem)
else:
return QtCore.QModelIndex()
def parent(self, index):
if not index.isValid():
return QtCore.QModelIndex()
childItem = index.internalPointer()
parentItem = childItem.parent()
if parentItem == self.rootItem:
return QtCore.QModelIndex()
return self.createIndex(parentItem.row(), 0, parentItem)
def rowCount(self, parent):
if parent.column() > 0:
return 0
if not parent.isValid():
parentItem = self.rootItem
else:
parentItem = parent.internalPointer()
return parentItem.childCount()
def initiateModel(self):
def add_children_to_tree(entries, parent_item):
for entry in entries:
row = []
for field in self.fields.keys():
val = getattr(entry, field)
if isinstance(val, list):
text = "; ".join(map(str, val))
else:
text = str(val)
row.append(text)
item = ORMItem(row, parent_item)
parent_item.appendChild(item)
with session_scope(self.engine) as session:
child_entries = (
session.query(self.entry_type)
.filter(
getattr(
getattr(self.entry_type, self.hierarchy_key), "is_"
)(entry.id)
)
.all()
)
if child_entries:
add_children_to_tree(child_entries, item)
with session_scope(self.engine) as session:
root_entries = (
session.query(self.entry_type)
.filter(
getattr(getattr(self.entry_type, self.hierarchy_key), "is_")(None)
)
.all()
)
if not isinstance(root_entries, list):
root_entries = [root_entries]
add_children_to_tree(root_entries, self.rootItem)