Pyside 用列表填充 QTreeView
Pyside populate QTreeView with list
我搜索了一段时间,但找不到对我的问题满意的答案。我需要在 QTreeView 中表示一个列表:
示例:
['.', ['A', 'B', 'C', 'D', 'E', 'F']], ['A', ['G', 'H']], ['A\G', []], ['A\H', ['K', 'L']], ['A\G\K', []], ['A\G\L', []], ['B', ['I', 'J']], ['B\I', []], ['B\J', []], ['C', []], ['D', []], ['E', []], ['F', ['M']], ['F\M', []]
应该这样表示:
.
|____A
|____H
|____G
|____K
|____L
|____B
|____I
|____J
|____C
|____D
|____E
|____F
|____M
编辑:可能数据准备不好。我也可以选择这种结构:
[".", "A", "A\G", "A\H", "A\H\K", "A\H\L", "B", "B\I", "B\J", "C", "D", "E", "F", "F\M"]
在控制台中可视化该结构不是问题,但我无法将其正确用于树视图。
这是我的节点 Class 和我的 TreeModel
from PySide.QtCore import *
from PySide.QtGui import *
import sys
class Node(object):
def __init__(self, name, parent=None):
self._name = name
self._children = []
self._parent = parent
if parent is not None:
parent.add_child(self)
def type_info(self):
return 'NODE'
def add_child(self, child):
self._children.append(child)
def insert_child(self, position, child):
if position < 0 or position > len(self._children):
return False
self._children.insert(position, child)
child._parent = self
return True
def remove_child(self, position):
if position < 0 or position > len(self._children):
return False
child = self._children.pop(position)
child._parent = None
return True
def name(self):
return self._name
def set_name(self, name):
self._name = name
def child(self, row):
return self._children[row]
def child_count(self):
return len(self._children)
def parent(self):
return self._parent
def row(self):
if self._parent is not None:
return self._parent._children.index(self)
def log(self, tab_level=-1):
output = ''
tab_level += 1
for i in range(tab_level):
output += '\t'
output += '|____' + self._name + '\n'
for child in self._children:
output += child.log(tab_level)
tab_level -= 1
return output
def __repr__(self):
return self.log()
class TemplateTreeModel(QAbstractItemModel):
def __init__(self, root, parent=None):
super(TemplateTreeModel, self).__init__(parent)
self._root_node = root
def rowCount(self, parent):
if not parent.isValid():
parent_node = self._root_node
else:
parent_node = parent.internalPointer()
return parent_node.child_count()
def columnCount(self, parent):
return 1
def data(self, index, role):
if not index.isValid():
print('root')
return None
node = index.internalPointer()
if role == Qt.DisplayRole or role == Qt.EditRole:
if index.column() == 0:
return node.name()
if role == Qt.DecorationRole:
if index.column() == 0:
type_info = node.type_info()
if type_info == 'FOLDER':
return QIcon('src/dir.png')
def setData(self, index, value, role=Qt.EditRole):
if index.isValid():
if role == Qt.EditRole:
node = index.internalPointer()
node.set_name(value)
return True
return False
def headerData(self, section, orientation, role):
if role == Qt.DisplayRole:
if section == 0:
return 'Templates'
else:
return 'Type'
def flags(self, index):
return Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsEditable
def parent(self, index):
node = self.get_node(index)
parent_node = node.parent()
if parent_node == self._root_node:
return QModelIndex()
return self.createIndex(parent_node.row(), 0, parent_node)
def index(self, row, column, parent):
parent_node = self.get_node(parent)
child_item = parent_node.child(row)
if child_item:
return self.createIndex(row, column, child_item)
else:
return QModelIndex
def get_node(self, index):
if index.isValid():
node = index.internalPointer()
if node:
return node
return self._root_node
def insertRows(self, position, rows, parent=QModelIndex()):
parent_node = self.get_node(parent)
self.beginInsertRows(parent, position, position + rows - 1)
for row in range(rows):
child_count = parent_node.child_count()
child_node = Node('untitled' + str(child_count))
success = parent_node.insert_child(position, child_node)
self.endInsertRows()
return success
def removeRows(self, position, rows, parent=QModelIndex()):
parent_node = self.get_node(parent)
self.beginRemoveRows(parent, position, position + rows - 1)
for row in range(rows):
success = parent_node.remove_child(position)
self.endRemoveRows()
return success
知道如何正确执行此操作吗?
for root, dirs in structure_list:
depth = root.split(os.sep)
print((len(depth) - 1) * '---', os.path.basename(root))
这就是我为控制台可视化所做的。大致 ;)
谢谢!
首先你的模型在某些部分实现不正确,例如根是一个不可见的元素所以我们必须添加它等等,所以我修改了你的代码并添加了一些方法,如下所示:
from PySide import QtCore, QtGui
class Node(object):
def __init__(self, name="", parent=None):
self._parent = parent
self._name = name
self._children = []
def children(self):
return self._children
def hasChildren(self):
return bool(self.children())
def parent(self):
return self._parent
def name(self):
return self._name
def set_name(self, name):
self._name = name
def type_info(self):
return 'NODE'
def columnCount(self):
return 1
def child_count(self):
return len(self._children)
def add_child(self, child):
self._children.append(child)
child._parent = self
def insert_child(self, position, child):
if 0 <= position < child_count:
self._children.insert(position, child)
child._parent = self
return True
return False
def remove_child(self, position):
if 0 <= position < len(self._children):
child = self._children.pop(position)
child._parent = None
return True
return False
def child(self, row):
if 0 <= row < self.child_count():
return self._children[row]
def row(self):
if self._parent is not None:
return self._parent._children.index(self)
return -1
def find_child_by_name(self, name):
for child in self._children:
if child.name() == name:
return child
return None
def log(self, tab_level=-1):
output = ''
tab_level += 1
for i in range(tab_level):
output += '\t'
output += '|____' + self._name + '\n'
for child in self._children:
output += child.log(tab_level)
tab_level -= 1
return output
def __repr__(self):
return self.log()
class TemplateTreeModel(QtCore.QAbstractItemModel):
def __init__(self, parent=None):
super().__init__(parent)
self._root_node = Node()
def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
if role == QtCore.Qt.DisplayRole:
if section == 0:
return 'Templates'
else:
return 'Type'
def index(self, row, column, parent):
if not self.hasIndex(row, column, parent):
return QtCore.QModelIndex()
node = parent.internalPointer() if parent.isValid() else self._root_node
if node.children:
return self.createIndex(row, column, node.child(row))
else:
return QtCore.QModelIndex()
def parent(self, child):
if not child.isValid():
return QtCore.QModelIndex()
node = child.internalPointer()
if node.row() >= 0:
return self.createIndex(node.row(), 0, node.parent())
return QtCore.QModelIndex()
def rowCount(self, parent=QtCore.QModelIndex()):
node = parent.internalPointer() if parent.isValid() else self._root_node
return node.child_count()
def columnCount(self, parent=QtCore.QModelIndex()):
return 1
def hasChildren(self, parent= QtCore.QModelIndex()):
node = parent.internalPointer() if parent.isValid() else self._root_node
return node.hasChildren()
def data(self, index: QtCore.QModelIndex, role=QtCore.Qt.DisplayRole):
if index.isValid() and role in (QtCore.Qt.DisplayRole, QtCore.Qt.EditRole, ):
node = index.internalPointer()
print(node)
return node.name()
def setData(self, index, value, role=QtCore.Qt.EditRole):
if role in (QtCore.Qt.EditRole,):
node = index.internalPointer()
node.set_name(value)
self.dataChanged.emit(index, index)
return True
return False
def flags(self, index: QtCore.QModelIndex):
return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEditable
def indexFromItem(self, it):
root_index = QtCore.QModelIndex()
if isinstance(it, Node):
parents = []
while it is not self._root_node:
parents.append(it)
it = it.parent()
root = self._root_node
for parent in reversed(parents):
root = root.find_child_by_name(parent.name())
root_index =self.index(root.row(), 0, root_index)
return root_index
def item_from_path(self, path, sep):
depth = path.split(sep)
root = self._root_node
for d in depth:
root = root.find_child_by_name(d)
if root is None: return None
return root
def appendRow(self, item, parent=None):
self.appendRows([item], parent)
def appendRows(self, items, parent=None):
if isinstance(items, list):
ix = self.indexFromItem(parent)
self.insertRows(self.rowCount(ix), items, parent)
def insertRows(self, position, items, parent=None):
parent_index = self.indexFromItem(parent)
self.beginInsertRows(parent_index, position, position + len(items) - 1)
if parent is None:
parent = self._root_node
for item in items:
parent.add_child(item)
self.endInsertRows()
另一方面,在您的初始列表中,模式未正确实现,因为根应该以 .\
开头,因此必须在必要时添加它。
structure_list = ['.', ['A', 'B', 'C', 'D', 'E', 'F']], ['A', ['G', 'H']], ['A\G', []], ['A\H', ['K', 'L']], ['A\G\K', []], ['A\G\L', []], ['B', ['I', 'J']], ['B\I', []], ['B\J', []], ['C', []], ['D', []], ['E', []], ['F', ['M']], ['F\M', []]
if __name__ == '__main__':
import os
import sys
app = QtGui.QApplication(sys.argv)
w = QtGui.QTreeView()
model = TemplateTreeModel()
w.setModel(model)
model.appendRow(Node("."))
for root, dirs in structure_list:
depth = root.split(os.sep)
if not root.startswith("."):
# append .\,
# by example if root is "A\G" then the results is:
# ".\A\G"
root = "." + os.sep + root
it = model.item_from_path(root, os.sep)
model.appendRows([Node(_dir) for _dir in dirs], it)
w.expandAll()
w.resize(640, 480)
w.show()
sys.exit(app.exec_())
我搜索了一段时间,但找不到对我的问题满意的答案。我需要在 QTreeView 中表示一个列表:
示例:
['.', ['A', 'B', 'C', 'D', 'E', 'F']], ['A', ['G', 'H']], ['A\G', []], ['A\H', ['K', 'L']], ['A\G\K', []], ['A\G\L', []], ['B', ['I', 'J']], ['B\I', []], ['B\J', []], ['C', []], ['D', []], ['E', []], ['F', ['M']], ['F\M', []]
应该这样表示:
.
|____A
|____H
|____G
|____K
|____L
|____B
|____I
|____J
|____C
|____D
|____E
|____F
|____M
编辑:可能数据准备不好。我也可以选择这种结构:
[".", "A", "A\G", "A\H", "A\H\K", "A\H\L", "B", "B\I", "B\J", "C", "D", "E", "F", "F\M"]
在控制台中可视化该结构不是问题,但我无法将其正确用于树视图。 这是我的节点 Class 和我的 TreeModel
from PySide.QtCore import *
from PySide.QtGui import *
import sys
class Node(object):
def __init__(self, name, parent=None):
self._name = name
self._children = []
self._parent = parent
if parent is not None:
parent.add_child(self)
def type_info(self):
return 'NODE'
def add_child(self, child):
self._children.append(child)
def insert_child(self, position, child):
if position < 0 or position > len(self._children):
return False
self._children.insert(position, child)
child._parent = self
return True
def remove_child(self, position):
if position < 0 or position > len(self._children):
return False
child = self._children.pop(position)
child._parent = None
return True
def name(self):
return self._name
def set_name(self, name):
self._name = name
def child(self, row):
return self._children[row]
def child_count(self):
return len(self._children)
def parent(self):
return self._parent
def row(self):
if self._parent is not None:
return self._parent._children.index(self)
def log(self, tab_level=-1):
output = ''
tab_level += 1
for i in range(tab_level):
output += '\t'
output += '|____' + self._name + '\n'
for child in self._children:
output += child.log(tab_level)
tab_level -= 1
return output
def __repr__(self):
return self.log()
class TemplateTreeModel(QAbstractItemModel):
def __init__(self, root, parent=None):
super(TemplateTreeModel, self).__init__(parent)
self._root_node = root
def rowCount(self, parent):
if not parent.isValid():
parent_node = self._root_node
else:
parent_node = parent.internalPointer()
return parent_node.child_count()
def columnCount(self, parent):
return 1
def data(self, index, role):
if not index.isValid():
print('root')
return None
node = index.internalPointer()
if role == Qt.DisplayRole or role == Qt.EditRole:
if index.column() == 0:
return node.name()
if role == Qt.DecorationRole:
if index.column() == 0:
type_info = node.type_info()
if type_info == 'FOLDER':
return QIcon('src/dir.png')
def setData(self, index, value, role=Qt.EditRole):
if index.isValid():
if role == Qt.EditRole:
node = index.internalPointer()
node.set_name(value)
return True
return False
def headerData(self, section, orientation, role):
if role == Qt.DisplayRole:
if section == 0:
return 'Templates'
else:
return 'Type'
def flags(self, index):
return Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsEditable
def parent(self, index):
node = self.get_node(index)
parent_node = node.parent()
if parent_node == self._root_node:
return QModelIndex()
return self.createIndex(parent_node.row(), 0, parent_node)
def index(self, row, column, parent):
parent_node = self.get_node(parent)
child_item = parent_node.child(row)
if child_item:
return self.createIndex(row, column, child_item)
else:
return QModelIndex
def get_node(self, index):
if index.isValid():
node = index.internalPointer()
if node:
return node
return self._root_node
def insertRows(self, position, rows, parent=QModelIndex()):
parent_node = self.get_node(parent)
self.beginInsertRows(parent, position, position + rows - 1)
for row in range(rows):
child_count = parent_node.child_count()
child_node = Node('untitled' + str(child_count))
success = parent_node.insert_child(position, child_node)
self.endInsertRows()
return success
def removeRows(self, position, rows, parent=QModelIndex()):
parent_node = self.get_node(parent)
self.beginRemoveRows(parent, position, position + rows - 1)
for row in range(rows):
success = parent_node.remove_child(position)
self.endRemoveRows()
return success
知道如何正确执行此操作吗?
for root, dirs in structure_list:
depth = root.split(os.sep)
print((len(depth) - 1) * '---', os.path.basename(root))
这就是我为控制台可视化所做的。大致 ;) 谢谢!
首先你的模型在某些部分实现不正确,例如根是一个不可见的元素所以我们必须添加它等等,所以我修改了你的代码并添加了一些方法,如下所示:
from PySide import QtCore, QtGui
class Node(object):
def __init__(self, name="", parent=None):
self._parent = parent
self._name = name
self._children = []
def children(self):
return self._children
def hasChildren(self):
return bool(self.children())
def parent(self):
return self._parent
def name(self):
return self._name
def set_name(self, name):
self._name = name
def type_info(self):
return 'NODE'
def columnCount(self):
return 1
def child_count(self):
return len(self._children)
def add_child(self, child):
self._children.append(child)
child._parent = self
def insert_child(self, position, child):
if 0 <= position < child_count:
self._children.insert(position, child)
child._parent = self
return True
return False
def remove_child(self, position):
if 0 <= position < len(self._children):
child = self._children.pop(position)
child._parent = None
return True
return False
def child(self, row):
if 0 <= row < self.child_count():
return self._children[row]
def row(self):
if self._parent is not None:
return self._parent._children.index(self)
return -1
def find_child_by_name(self, name):
for child in self._children:
if child.name() == name:
return child
return None
def log(self, tab_level=-1):
output = ''
tab_level += 1
for i in range(tab_level):
output += '\t'
output += '|____' + self._name + '\n'
for child in self._children:
output += child.log(tab_level)
tab_level -= 1
return output
def __repr__(self):
return self.log()
class TemplateTreeModel(QtCore.QAbstractItemModel):
def __init__(self, parent=None):
super().__init__(parent)
self._root_node = Node()
def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
if role == QtCore.Qt.DisplayRole:
if section == 0:
return 'Templates'
else:
return 'Type'
def index(self, row, column, parent):
if not self.hasIndex(row, column, parent):
return QtCore.QModelIndex()
node = parent.internalPointer() if parent.isValid() else self._root_node
if node.children:
return self.createIndex(row, column, node.child(row))
else:
return QtCore.QModelIndex()
def parent(self, child):
if not child.isValid():
return QtCore.QModelIndex()
node = child.internalPointer()
if node.row() >= 0:
return self.createIndex(node.row(), 0, node.parent())
return QtCore.QModelIndex()
def rowCount(self, parent=QtCore.QModelIndex()):
node = parent.internalPointer() if parent.isValid() else self._root_node
return node.child_count()
def columnCount(self, parent=QtCore.QModelIndex()):
return 1
def hasChildren(self, parent= QtCore.QModelIndex()):
node = parent.internalPointer() if parent.isValid() else self._root_node
return node.hasChildren()
def data(self, index: QtCore.QModelIndex, role=QtCore.Qt.DisplayRole):
if index.isValid() and role in (QtCore.Qt.DisplayRole, QtCore.Qt.EditRole, ):
node = index.internalPointer()
print(node)
return node.name()
def setData(self, index, value, role=QtCore.Qt.EditRole):
if role in (QtCore.Qt.EditRole,):
node = index.internalPointer()
node.set_name(value)
self.dataChanged.emit(index, index)
return True
return False
def flags(self, index: QtCore.QModelIndex):
return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEditable
def indexFromItem(self, it):
root_index = QtCore.QModelIndex()
if isinstance(it, Node):
parents = []
while it is not self._root_node:
parents.append(it)
it = it.parent()
root = self._root_node
for parent in reversed(parents):
root = root.find_child_by_name(parent.name())
root_index =self.index(root.row(), 0, root_index)
return root_index
def item_from_path(self, path, sep):
depth = path.split(sep)
root = self._root_node
for d in depth:
root = root.find_child_by_name(d)
if root is None: return None
return root
def appendRow(self, item, parent=None):
self.appendRows([item], parent)
def appendRows(self, items, parent=None):
if isinstance(items, list):
ix = self.indexFromItem(parent)
self.insertRows(self.rowCount(ix), items, parent)
def insertRows(self, position, items, parent=None):
parent_index = self.indexFromItem(parent)
self.beginInsertRows(parent_index, position, position + len(items) - 1)
if parent is None:
parent = self._root_node
for item in items:
parent.add_child(item)
self.endInsertRows()
另一方面,在您的初始列表中,模式未正确实现,因为根应该以 .\
开头,因此必须在必要时添加它。
structure_list = ['.', ['A', 'B', 'C', 'D', 'E', 'F']], ['A', ['G', 'H']], ['A\G', []], ['A\H', ['K', 'L']], ['A\G\K', []], ['A\G\L', []], ['B', ['I', 'J']], ['B\I', []], ['B\J', []], ['C', []], ['D', []], ['E', []], ['F', ['M']], ['F\M', []]
if __name__ == '__main__':
import os
import sys
app = QtGui.QApplication(sys.argv)
w = QtGui.QTreeView()
model = TemplateTreeModel()
w.setModel(model)
model.appendRow(Node("."))
for root, dirs in structure_list:
depth = root.split(os.sep)
if not root.startswith("."):
# append .\,
# by example if root is "A\G" then the results is:
# ".\A\G"
root = "." + os.sep + root
it = model.item_from_path(root, os.sep)
model.appendRows([Node(_dir) for _dir in dirs], it)
w.expandAll()
w.resize(640, 480)
w.show()
sys.exit(app.exec_())