1#!/usr/bin/env python 2 3 4############################################################################# 5## 6## Copyright (C) 2013 Riverbank Computing Limited. 7## Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). 8## All rights reserved. 9## 10## This file is part of the examples of PyQt. 11## 12## $QT_BEGIN_LICENSE:BSD$ 13## You may use this file under the terms of the BSD license as follows: 14## 15## "Redistribution and use in source and binary forms, with or without 16## modification, are permitted provided that the following conditions are 17## met: 18## * Redistributions of source code must retain the above copyright 19## notice, this list of conditions and the following disclaimer. 20## * Redistributions in binary form must reproduce the above copyright 21## notice, this list of conditions and the following disclaimer in 22## the documentation and/or other materials provided with the 23## distribution. 24## * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor 25## the names of its contributors may be used to endorse or promote 26## products derived from this software without specific prior written 27## permission. 28## 29## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 30## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 31## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 32## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 33## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 34## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 35## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 36## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 37## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 38## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 39## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." 40## $QT_END_LICENSE$ 41## 42############################################################################# 43 44 45from PyQt5.QtCore import QAbstractItemModel, QFile, QIODevice, QModelIndex, Qt 46from PyQt5.QtWidgets import QApplication, QFileDialog, QMainWindow, QTreeView 47from PyQt5.QtXml import QDomDocument 48 49 50class DomItem(object): 51 def __init__(self, node, row, parent=None): 52 self.domNode = node 53 # Record the item's location within its parent. 54 self.rowNumber = row 55 self.parentItem = parent 56 self.childItems = {} 57 58 def node(self): 59 return self.domNode 60 61 def parent(self): 62 return self.parentItem 63 64 def child(self, i): 65 if i in self.childItems: 66 return self.childItems[i] 67 68 if i >= 0 and i < self.domNode.childNodes().count(): 69 childNode = self.domNode.childNodes().item(i) 70 childItem = DomItem(childNode, i, self) 71 self.childItems[i] = childItem 72 return childItem 73 74 return None 75 76 def row(self): 77 return self.rowNumber 78 79 80class DomModel(QAbstractItemModel): 81 def __init__(self, document, parent=None): 82 super(DomModel, self).__init__(parent) 83 84 self.domDocument = document 85 86 self.rootItem = DomItem(self.domDocument, 0) 87 88 def columnCount(self, parent): 89 return 3 90 91 def data(self, index, role): 92 if not index.isValid(): 93 return None 94 95 if role != Qt.DisplayRole: 96 return None 97 98 item = index.internalPointer() 99 100 node = item.node() 101 attributes = [] 102 attributeMap = node.attributes() 103 104 if index.column() == 0: 105 return node.nodeName() 106 107 elif index.column() == 1: 108 for i in range(0, attributeMap.count()): 109 attribute = attributeMap.item(i) 110 attributes.append(attribute.nodeName() + '="' + 111 attribute.nodeValue() + '"') 112 113 return " ".join(attributes) 114 115 if index.column() == 2: 116 value = node.nodeValue() 117 if value is None: 118 return '' 119 120 return ' '.join(node.nodeValue().split('\n')) 121 122 return None 123 124 def flags(self, index): 125 if not index.isValid(): 126 return Qt.NoItemFlags 127 128 return Qt.ItemIsEnabled | Qt.ItemIsSelectable 129 130 def headerData(self, section, orientation, role): 131 if orientation == Qt.Horizontal and role == Qt.DisplayRole: 132 if section == 0: 133 return "Name" 134 135 if section == 1: 136 return "Attributes" 137 138 if section == 2: 139 return "Value" 140 141 return None 142 143 def index(self, row, column, parent): 144 if not self.hasIndex(row, column, parent): 145 return QModelIndex() 146 147 if not parent.isValid(): 148 parentItem = self.rootItem 149 else: 150 parentItem = parent.internalPointer() 151 152 childItem = parentItem.child(row) 153 if childItem: 154 return self.createIndex(row, column, childItem) 155 else: 156 return QModelIndex() 157 158 def parent(self, child): 159 if not child.isValid(): 160 return QModelIndex() 161 162 childItem = child.internalPointer() 163 parentItem = childItem.parent() 164 165 if not parentItem or parentItem == self.rootItem: 166 return QModelIndex() 167 168 return self.createIndex(parentItem.row(), 0, parentItem) 169 170 def rowCount(self, parent): 171 if parent.column() > 0: 172 return 0 173 174 if not parent.isValid(): 175 parentItem = self.rootItem 176 else: 177 parentItem = parent.internalPointer() 178 179 return parentItem.node().childNodes().count() 180 181 182class MainWindow(QMainWindow): 183 def __init__(self): 184 super(MainWindow, self).__init__() 185 186 self.fileMenu = self.menuBar().addMenu("&File") 187 self.fileMenu.addAction("&Open...", self.openFile, "Ctrl+O") 188 self.fileMenu.addAction("E&xit", self.close, "Ctrl+Q") 189 190 self.xmlPath = "" 191 self.model = DomModel(QDomDocument(), self) 192 self.view = QTreeView(self) 193 self.view.setModel(self.model) 194 195 self.setCentralWidget(self.view) 196 self.setWindowTitle("Simple DOM Model") 197 198 def openFile(self): 199 filePath, _ = QFileDialog.getOpenFileName(self, "Open File", 200 self.xmlPath, "XML files (*.xml);;HTML files (*.html);;" 201 "SVG files (*.svg);;User Interface files (*.ui)") 202 203 if filePath: 204 f = QFile(filePath) 205 if f.open(QIODevice.ReadOnly): 206 document = QDomDocument() 207 if document.setContent(f): 208 newModel = DomModel(document, self) 209 self.view.setModel(newModel) 210 self.model = newModel 211 self.xmlPath = filePath 212 213 f.close() 214 215 216if __name__ == '__main__': 217 218 import sys 219 220 app = QApplication(sys.argv) 221 window = MainWindow() 222 window.resize(640, 480) 223 window.show() 224 sys.exit(app.exec_()) 225