如何保存 PySide 树视图模型结构
Posted
技术标签:
【中文标题】如何保存 PySide 树视图模型结构【英文标题】:How to save PySide tree view model structure 【发布时间】:2013-12-23 13:47:49 【问题描述】:这是其他 SO 问题 QTreeView with custom items 的链接,QTreeView 示例在哪里。
请,谁能在这个例子中解释一下,如何从树视图中保存树结构。
我可以从中提取标签名称的 QAbstractItemModel 类以及我可以再次加载的结构吗?
如果是这样,我如何访问节点?除了索引还有其他方法吗?
编辑(来自链接):
import sys
from PySide import QtGui, QtCore
#-------------------------------------------------------------------------------
# my test data
class Icon():
def __init__(self, icon, tooltip):
self.pixmap = QtGui.QPixmap(icon)
self.tooltip = tooltip
#-------------------------------------------------------------------------------
# my test data
class MyData():
def __init__(self, txt, parent=None):
self.txt = txt
self.tooltip = None
self.parent = parent
self.child = []
self.icon = []
self.index = None
self.widget = None
#---------------------------------------------------------------------------
def position(self):
position = 0
if self.parent is not None:
count = 0
children = self.parent.child
for child in children:
if child == self:
position = count
break
count += 1
return position
#---------------------------------------------------------------------------
# test initialization
@staticmethod
def init():
root = MyData("root")
root.icon.append(Icon("icon.png", "ToolTip icon.png"))
root.tooltip = "root tooltip"
for i in range(0, 2):
child1 = MyData("child %i" % (i), root)
child1.icon.append(Icon("icon1.png", "ToolTip icon1.png"))
child1.tooltip = "child1 tooltip"
root.child.append(child1)
for x in range(0, 2):
child2 = MyData("child %i %i" % (i, x), child1)
child2.icon.append(Icon("icon1.png", "ToolTip icon1.png"))
child2.icon.append(Icon("icon2.png", "ToolTip icon2.png"))
child2.tooltip = "child2 tooltip"
child1.child.append(child2)
return root
#-------------------------------------------------------------------------------
class TreeViewModel(QtCore.QAbstractItemModel):
#---------------------------------------------------------------------------
def __init__(self, tree):
super(TreeViewModel, self).__init__()
self.__tree = tree
self.__current = tree
self.__view = None
#---------------------------------------------------------------------------
def flags(self, index):
flag = QtCore.Qt.ItemIsEnabled
if index.isValid():
flag |= QtCore.Qt.ItemIsSelectable \
| QtCore.Qt.ItemIsUserCheckable \
| QtCore.Qt.ItemIsEditable \
| QtCore.Qt.ItemIsDragEnabled \
| QtCore.Qt.ItemIsDropEnabled
return flag
#---------------------------------------------------------------------------
def index(self, row, column, parent=QtCore.QModelIndex()):
node = QtCore.QModelIndex()
if parent.isValid():
nodeS = parent.internalPointer()
nodeX = nodeS.child[row]
node = self.__createIndex(row, column, nodeX)
else:
node = self.__createIndex(row, column, self.__tree)
return node
#---------------------------------------------------------------------------
def parent(self, index):
node = QtCore.QModelIndex()
if index.isValid():
nodeS = index.internalPointer()
parent = nodeS.parent
if parent is not None:
node = self.__createIndex(parent.position(), 0, parent)
return node
#---------------------------------------------------------------------------
def rowCount(self, index=QtCore.QModelIndex()):
count = 1
node = index.internalPointer()
if node is not None:
count = len(node.child)
return count
#---------------------------------------------------------------------------
def columnCount(self, index=QtCore.QModelIndex()):
return 1
#---------------------------------------------------------------------------
def data(self, index, role=QtCore.Qt.DisplayRole):
data = None
return data
#---------------------------------------------------------------------------
def setView(self, view):
self.__view = view
#---------------------------------------------------------------------------
def __createIndex(self, row, column, node):
if node.index == None:
index = self.createIndex(row, column, node)
node.index = index
if node.widget is None:
node.widget = Widget(node)
self.__view.setIndexWidget(index, node.widget)
return node.index
#-------------------------------------------------------------------------------
class TreeView(QtGui.QTreeView):
#---------------------------------------------------------------------------
def __init__(self, model, parent=None):
super(TreeView, self).__init__(parent)
self.setModel(model)
model.setView(self)
root = model.index(0, 0)
self.setCurrentIndex(root)
self.setHeaderHidden(True)
#---------------------------------------------------------------------------
def keyPressEvent(self, event):
k = event.key()
if k == QtCore.Qt.Key_F2:
self.__editMode()
super(TreeView, self).keyPressEvent(event)
#---------------------------------------------------------------------------
def __editMode(self):
index = self.currentIndex()
node = index.internalPointer()
node.widget.editMode(True, True)
#-------------------------------------------------------------------------------
class Label(QtGui.QLabel):
#---------------------------------------------------------------------------
def __init__(self, parent, text):
super(Label, self).__init__(text)
self.__parent = parent
#---------------------------------------------------------------------------
def mouseDoubleClickEvent(self, event):
#print("mouseDoubleClickEvent")
if self.__parent is not None:
self.__parent.editMode(True, True)
else:
super(Label, self).mouseDoubleClickEvent(event)
#-------------------------------------------------------------------------------
class LineEdit(QtGui.QLineEdit):
#---------------------------------------------------------------------------
def __init__(self, parent, text):
super(LineEdit, self).__init__(text)
self.__parent = parent
self.editingFinished.connect(self.__editingFinished)
#---------------------------------------------------------------------------
def keyPressEvent(self, event):
k = event.key()
if k == QtCore.Qt.Key_Escape:
print("ESC 2")
self.__editingFinished(False)
super(LineEdit, self).keyPressEvent(event)
#---------------------------------------------------------------------------
def __editingFinished(self, bCopy=True):
print("editingFinished")
self.__parent.editMode(False, bCopy)
#-------------------------------------------------------------------------------
class Widget(QtGui.QWidget):
#---------------------------------------------------------------------------
def __init__(self, node):
super(Widget, self).__init__()
self.autoFillBackground()
self.__node = node
self.__bEditMode = False
self.__txt = None
self.__create(self.__node, self.__bEditMode)
#---------------------------------------------------------------------------
def __create(self, node, bEditMode):
layout = QtGui.QHBoxLayout()
for icon in node.icon:
label = Label(None, node.txt)
label.setPixmap(icon.pixmap)
label.setToolTip("label tooltip %s %s" % (node.txt, icon.tooltip))
layout.addWidget(label)
self.__changeTxt(layout, node, bEditMode, False)
self.setLayout(layout)
#---------------------------------------------------------------------------
def __changeTxt(self, layout, node, bEditMode, bCopy):
if self.__txt is not None:
if bCopy:
node.txt = self.__txt.text()
if isinstance(self.__txt, LineEdit):
self.__txt.deselect()
self.__txt.hide()
layout.removeWidget(self.__txt)
self.__txt = None
if bEditMode:
self.__txt = LineEdit(self, node.txt)
self.__txt.setFrame(False)
self.__txt.selectAll()
QtCore.QTimer.singleShot(0, self.__txt, QtCore.SLOT('setFocus()'));
else:
self.__txt = Label(self, node.txt)
self.__txt.setToolTip("Text tooltip %s %s" % (node.txt, node.tooltip))
layout.addWidget(self.__txt, 1)
#---------------------------------------------------------------------------
def editMode(self, bEditMode, bCopy):
if self.__bEditMode != bEditMode:
self.__bEditMode = bEditMode
layout = self.layout()
self.__changeTxt(layout, self.__node, bEditMode, bCopy)
#-------------------------------------------------------------------------------
class MyTree(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MyTree, self).__init__(parent)
data = MyData.init()
frame = QtGui.QFrame();
frame.setLayout( QtGui.QHBoxLayout() );
treeViewModel = TreeViewModel(data)
treeView = TreeView(treeViewModel)
frame.layout().addWidget( treeView );
self.setCentralWidget(frame)
#-------------------------------------------------------------------------------
def main():
app = QtGui.QApplication(sys.argv)
form = MyTree()
form.show()
app.exec_()
if __name__ == '__main__':
main()
【问题讨论】:
【参考方案1】:你的问题很不清楚,因为你没有说你希望树结构以什么形式保存。
假设您并不真正关心,并且 XML 是可以接受的,您可以查看 Qt 文档中的 Simple DOM Model Example。
Qt 文档中的大多数示例已移植到 PyQt,并提供了源代码,可以从 here 下载。简单 DOM 模型示例可以在 examples/itemviews
下找到。
编辑:
我忽略了您正在使用 PySide。 PySide 的等效移植示例可以在here 或PySide source code 下的sources/pyside-examples/examples/itemviews
中找到。
更新:
这是一个使用xml.etree 序列化树的简单示例:
import sip
sip.setapi('QString', 2)
from xml.etree import cElementTree as etree
from PyQt4 import QtGui, QtCore
class Window(QtGui.QWidget):
def __init__(self, xml):
QtGui.QWidget.__init__(self)
self.tree = QtGui.QTreeWidget(self)
self.tree.header().hide()
self.importTree(xml)
self.button = QtGui.QPushButton('Export', self)
self.button.clicked[()].connect(self.exportTree)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.tree)
layout.addWidget(self.button)
def importTree(self, xml):
def build(item, root):
for element in root.getchildren():
child = QtGui.QTreeWidgetItem(
item, [element.attrib['text']])
child.setFlags(
child.flags() | QtCore.Qt.ItemIsEditable)
build(child, element)
item.setExpanded(True)
root = etree.fromstring(xml)
build(self.tree.invisibleRootItem(), root)
def exportTree(self):
def build(item, root):
for row in range(item.childCount()):
child = item.child(row)
element = etree.SubElement(
root, 'node', text=child.text(0))
build(child, element)
root = etree.Element('root')
build(self.tree.invisibleRootItem(), root)
from xml.dom import minidom
print(minidom.parseString(etree.tostring(root)).toprettyxml())
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window("""\
<?xml version="1.0" ?>
<root>
<node text="Child (0)">
<node text="Child (0)">
<node text="Child (0)"/>
<node text="Child (1)"/>
</node>
<node text="Child (1)">
<node text="Child (0)"/>
<node text="Child (1)"/>
</node>
</node>
<node text="Child (1)">
<node text="Child (0)">
<node text="Child (0)"/>
<node text="Child (1)"/>
</node>
<node text="Child (1)">
<node text="Child (0)"/>
<node text="Child (1)"/>
</node>
</node>
</root>
""")
window.setGeometry(800, 300, 300, 300)
window.show()
sys.exit(app.exec_())
【讨论】:
是的,试过这个例子,但我想编辑树项目名称,然后再次保存结构。请以我提到的这个为例,你会弄清楚我需要什么。谢谢。 @Alex。我真的没有看到您发布的代码的相关性。我建议您先编写一个基于QTreeWidget 的非常简单 示例(即没有自定义模型或时髦的编辑小部件)。首先,您需要的只是一个基本的递归函数,它遍历树并打印每个项目的文本(带有适当的缩进)。一旦你有了它,它应该很容易适应,比如说,将输出保存为 xml。 是的,我会轻松构建递归函数,但无法确定树的生长位置:( @Alex。你想要的是恰当命名的invisibleRootItem。无论如何,我在我的答案中添加了一个简单的示例,应该可以帮助您入门。以上是关于如何保存 PySide 树视图模型结构的主要内容,如果未能解决你的问题,请参考以下文章