Python PyQt:使用QDataStream保存本机QTreeWidgets
我最近花了一些时间研究如何在PyQt中使用QDataStream和QTreeWidget。我从来没有找到具体的例子来做这件事,而QDataStream的pyqt文档一般来说似乎非常稀少。所以我想我应该在这里发布一个问题作为面包屑线索,以防其他人需要提示。我会等一等,以防有人想跳进去试一试,我会用我自己的努力再发回来 问题是:在PyQt中,如何使用QDataStream将QTreeWidgetItems作为本机QT对象保存到文件中,然后将该文件读回以完全恢复保存的树结构Python PyQt:使用QDataStream保存本机QTreeWidgets,python,qt,python-2.7,pyqt,pyqt4,Python,Qt,Python 2.7,Pyqt,Pyqt4,我最近花了一些时间研究如何在PyQt中使用QDataStream和QTreeWidget。我从来没有找到具体的例子来做这件事,而QDataStream的pyqt文档一般来说似乎非常稀少。所以我想我应该在这里发布一个问题作为面包屑线索,以防其他人需要提示。我会等一等,以防有人想跳进去试一试,我会用我自己的努力再发回来 问题是:在PyQt中,如何使用QDataStream将QTreeWidgetItems作为本机QT对象保存到文件中,然后将该文件读回以完全恢复保存的树结构 EricQTreeWidg
EricQTreeWidget是一个骗人的东西。您保存的是一个通用的QAbstractItemModel(
treeWidget->model()
)——毕竟,QTreeWidget
是一个视图,并且有一个内置的模型。现在,这些模型的项是简单的QVariant
s,这些是简单的Python类型,但也完全由QDataStream::operator支持。在一个类似的问题中,我编写了一个简单的演示,序列化为xml
同样的代码可以很容易地适应QDataStream。我并不是真的建议将此作为一种解决方案(可能有几十种不同的方法来实现相同的目标),但它至少提供了一个工作示例:
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.button = QtGui.QPushButton('Export', self)
self.button.clicked[()].connect(self.exportTree)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.tree)
layout.addWidget(self.button)
self._array = QtCore.QByteArray()
self._buffer = QtCore.QBuffer(self._array, self)
self._buffer.open(QtCore.QIODevice.ReadWrite)
self._datastream = QtCore.QDataStream(self._buffer)
self.importTree(xml)
def importTree(self, xml):
def build(item, root):
for element in root.getchildren():
child = QtGui.QTreeWidgetItem(item)
data = element.attrib['data'].encode('ascii')
self._array.swap(self._array.fromBase64(data))
self._buffer.reset()
self._datastream >> child
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)
self._array.clear()
self._buffer.reset()
self._datastream << child
data = self._array.toBase64().data().decode('ascii')
element = etree.SubElement(root, 'node', data=data)
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 data="AAAAAQAAAAEAAAAJAAAAQwAB/////wAA
AAAAAAAAAAEAAAAKAAAAAAYAUgBlAGQ=">
<node data="AAAAAQAAAAEAAAAJAAAAQwAB//8AAP//
//8AAAAAAAEAAAAKAAAAAAgAQwB5AGEAbg==">
<node data="AAAAAQAAAAEAAAAJAAAAQwAB//8AAICA
AAAAAAAAAAEAAAAKAAAAAAoARwByAGUAZQBu"/>
<node data="AAAAAQAAAAEAAAAJAAAAQwAB//8AAAAA
//8AAAAAAAEAAAAKAAAAAAgAQgBsAHUAZQ=="/>
</node>
<node data="AAAAAQAAAAEAAAAJAAAAQwAB/////6Wl
AAAAAAAAAAEAAAAKAAAAAAwATwByAGEAbgBnAGU=">
<node data="AAAAAQAAAAEAAAAJAAAAQwAB//+AgAAA
gIAAAAAAAAEAAAAKAAAAAAwAUAB1AHIAcABsAGU="/>
<node data="AAAAAQAAAAEAAAAJAAAAQwAB//8AAICA
AAAAAAAAAAEAAAAKAAAAAAoARwByAGUAZQBu"/>
</node>
</node>
<node data="AAAAAQAAAAEAAAAJAAAAQwAB//8AAP//
//8AAAAAAAEAAAAKAAAAAAgAQwB5AGEAbg==">
<node data="AAAAAQAAAAEAAAAJAAAAQwAB/////6Wl
AAAAAAAAAAEAAAAKAAAAAAwATwByAGEAbgBnAGU=">
<node data="AAAAAQAAAAEAAAAJAAAAQwAB//8AAP//
//8AAAAAAAEAAAAKAAAAAAgAQwB5AGEAbg=="/>
<node data="AAAAAQAAAAEAAAAJAAAAQwAB//+AgAAA
gIAAAAAAAAEAAAAKAAAAAAwAUAB1AHIAcABsAGU="/>
</node>
<node data="AAAAAQAAAAEAAAAJAAAAQwAB//8AAICA
AAAAAAAAAAEAAAAKAAAAAAoARwByAGUAZQBu">
<node data="AAAAAQAAAAEAAAAJAAAAQwAB//8AAAAA
//8AAAAAAAEAAAAKAAAAAAgAQgBsAHUAZQ=="/>
<node data="AAAAAQAAAAEAAAAJAAAAQwAB/////wAA
AAAAAAAAAAEAAAAKAAAAAAYAUgBlAGQ="/>
</node>
</node>
</root>
""")
window.setGeometry(800, 300, 300, 300)
window.show()
sys.exit(app.exec_())
导入sip
sip.setapi('QString',2)
从xml.etree导入cElementTree作为etree
从PyQt4导入QtGui、QtCore
类窗口(QtGui.QWidget):
定义初始化(self,xml):
QtGui.QWidget.\uuuuu init\uuuuuuu(self)
self.tree=QtGui.QTreeWidget(self)
self.tree.header().hide()
self.button=QtGui.QPushButton('Export',self)
self.button.clicked[()].connect(self.exportTree)
layout=QtGui.QVBoxLayout(self)
layout.addWidget(self.tree)
layout.addWidget(self.button)
self.\u数组=QtCore.QByteArray()
self.\u buffer=QtCore.QBuffer(self.\u数组,self)
self.\u buffer.open(QtCore.QIODevice.ReadWrite)
self.\u datastream=QtCore.QDataStream(self.\u缓冲区)
self.importTree(xml)
def importTree(self,xml):
def生成(项,根):
对于root.getchildren()中的元素:
child=QtGui.QTreeWidgetItem(项目)
data=element.attrib['data'].encode('ascii')
self.\u array.swap(self.\u array.fromBase64(数据))
self.\u buffer.reset()
self.\u数据流>>子对象
构建(子元素、元素)
item.setExpanded(True)
root=etree.fromstring(xml)
生成(self.tree.invisibleRootItem(),根)
def导出树(自身):
def生成(项,根):
对于范围(item.childCount())中的行:
子项=项。子项(行)
self.\u array.clear()
self.\u buffer.reset()
self.\u datastream我将继续展示我使用的方法。希望我在知道自己希望如何解决自己的问题时不会有不公平的优势:-)
如果有人对此有更干净或更具蟒蛇风格的看法,欢迎跟进。谢谢
import sys,os.path
from PyQt4 import QtGui, QtCore
class TreeExperiment(QtGui.QWidget):
def __init__(self,parent=None):
QtGui.QWidget.__init__(self,parent)
self.tree=QtGui.QTreeWidget(self) #
self.tree.setObjectName("treeWidget") #
self.add_button=QtGui.QPushButton("Add", self) # Initialize a simple
self.save_button=QtGui.QPushButton("Save", self) # form containing a
gridlayout = QtGui.QGridLayout(self) # treeWidget, an
gridlayout.addWidget(self.tree,1,0,1,9) # 'Add' button, and a
gridlayout.addWidget(self.add_button,2,0,2,3) # 'Save' button
gridlayout.addWidget(self.save_button,2,3,2,3) #
self.tree.headerItem().setText(0,"Label") #
if os.path.isfile('native_tree_save.qfile'):
# First look for a previously saved tree. If found, define
# it as a QFile named 'file', open it, and define a datastream
# to read from it.
#
# Each tree node is saved to and read from the file in pairs:
# first, the QTreeWidgetItem itself, then the number of children
# the item has so that the tree structure can be re-created
#
# The first item is added directly as the root for simplicity,
# and is sent to the function which begins the tree reconstruction
file = QtCore.QFile('native_tree_save.qfile')
file.open(QtCore.QIODevice.ReadOnly)
datastream = QtCore.QDataStream(file)
child=QtGui.QTreeWidgetItem(self.tree.invisibleRootItem())
child.read(datastream)
num_childs=datastream.readUInt32()
self.restore_item(datastream,child,num_childs)
else: # Otherwise if this is the first use, create a root item
new_item=QtGui.QTreeWidgetItem(self.tree)
self.tree.setCurrentItem(self.tree.topLevelItem(0))
self.tree.currentItem().setText(0,'root')
self.tree.setItemSelected(self.tree.topLevelItem(0),1)
self.tree.setCurrentItem(self.tree.topLevelItem(0))
self.connect(self.add_button, QtCore.SIGNAL("clicked()"), self.add_item)
self.connect(self.save_button, QtCore.SIGNAL("clicked()"), self.save_tree)
self.added_item_count=0
def add_item(self): # Adds an item to whatever is selected
self.added_item_count+=1
label=str(self.added_item_count)
new_item=QtGui.QTreeWidgetItem(self.tree.currentItem())
new_item.setText(0,label)
self.tree.setCurrentItem(new_item)
def restore_item(self,datastream,item,num_childs):
for i in range(0, num_childs):
child=QtGui.QTreeWidgetItem(item)
child.read(datastream)
num_childs=datastream.readUInt32()
self.restore_item(datastream,child,num_childs)
def save_item(self,item,datastream):
num_childs=item.childCount()
for i in range(0,num_childs):
child = item.child(i)
child.write(datastream)
num_childs=child.childCount()
datastream.writeUInt32(num_childs)
self.save_item(child,datastream)
def save_tree(self):
file = QtCore.QFile('native_tree_save.qfile')
file.open(QtCore.QIODevice.WriteOnly)
datastream = QtCore.QDataStream(file)
self.save_item(self.tree.invisibleRootItem(),datastream)
if __name__=='__main__':
app = QtGui.QApplication(sys.argv)
window = TreeExperiment()
window.resize(200, 120)
window.show()
sys.exit(app.exec_())
谢谢——我同意,这是对解决方案的一个很好的描述,希望我在谈论树篱而不是它的项目时没有欺骗。我得出的解决方案依赖于存储项并使用它们填充新树的结构,这是完全正确的。您想提供几行python代码吗?我看到你在C++中说话,但我不能让这些操作符工作。使用QDataStream功能是我花了一段时间才理解的技巧之一。@Ericmeyers明天会做,我偶尔会使用Python。