2014-02-08 3 views
3

Недавно я потратил некоторое время на разработку того, как использовать QDataStream с QTreeWidget в PyQt. Я никогда не нашел конкретных примеров для этого, и документация pyqt для QDataStream кажется довольно скудной вообще. Поэтому я подумал, что я поставил бы здесь вопрос, как тростник в случае, если кто-то еще на линии нуждается в намеке. Я подожду немного, если кто-то захочет вскочить и сделать снимок, и я отправлю обратно немного с моими собственными усилиями.PyQt: Сохранение собственных QTreeWidgets с использованием QDataStream

Вопрос: В PyQt, как я могу использовать QDataStream для сохранения QTreeWidgetItems в файл как собственные объекты QT, а затем прочитать файл обратно, чтобы восстановить древовидную структуру точно так же, как она была сохранена?

Эрик

ответ

1

Я буду идти вперед и показать метод, который я использовал. Надеюсь, у меня нет несправедливого преимущества в понимании того, как я решил, что моя собственная проблема решена :-)

Если у кого-то есть более чистая или более питонская, возьмите это, пожалуйста, будьте готовы следить. Благодаря!

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_()) 
0

QTreeWidget является отвлекающим маневром. То, что вы сохраняете, является общим QAbstractItemModel (treeWidget->model()). В конце концов, QTreeWidget - это представление и имеет встроенную модель. Теперь эти элементы модели просто QVariant s, и это просто типы Python, но также полностью поддерживаемые QDataStream::operator<<. Все, что вам нужно, это выбрать обход дерева (сначала глубину, ширину или что-то еще), а также сбросить элементы и их глубину в дереве в поток. Когда вы читаете поток, это достаточная информация для восстановления дерева.

+0

Спасибо. Я согласен, что это хорошее описание решения. Надеюсь, я не обманчиво говорил о treeWidget, а не о его элементах. Верно, что решение, к которому я пришел, зависело от хранения элементов и их использования для заполнения структуры нового treeWidget. Вы хотите предложить несколько строк кода python? Я вижу, что вы говорите на C++, но я не смог заставить этих операторов работать. Использование функциональности QDataStream было одним из трюков, которые занимали меня некоторое время, чтобы понять. –

+0

@EricMyers Будет делать завтра, я иногда использую Python. –

2

В том же вопросе я написал простую демоверсию, которая сериализуется в 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_()) 
Смежные вопросы