I have this code that works so far, to have a direct tree view of an xml file:
from PyQt5.QtCore import QAbstractItemModel, QFile, QIODevice, QModelIndex, Qt
from PyQt5.QtWidgets import QApplication, QFileDialog, QMainWindow, QTreeView
from PyQt5.QtXml import QDomDocument, QDomElement
class DomItem(object):
def __init__(self, node, row, parent=None):
self.domNode = node
# Record the item's location within its parent.
self.rowNumber = row
self.parentItem = parent
self.childItems = {}
def node(self):
return self.domNode
def parent(self):
return self.parentItem
def child(self, i):
if i in self.childItems:
return self.childItems[i]
if 0 <= i < self.domNode.childNodes().count():
childNode = self.domNode.childNodes().item(i)
childItem = DomItem(childNode, i, self)
self.childItems[i] = childItem
return childItem
return None
def row(self):
return self.rowNumber
class DomModel(QAbstractItemModel):
def __init__(self, document, parent=None):
super(DomModel, self).__init__(parent)
self.domDocument = document
self.rootItem = DomItem(self.domDocument, 0)
def columnCount(self, parent):
# return 3
return 2
def data(self, index, role):
if not index.isValid():
return None
if role != Qt.DisplayRole:
return None
item = index.internalPointer()
node = item.node()
if index.column() == 0:
# print(node.nodeName())
if node.nodeName() != '#text':
return node.nodeName()
else:
return None
if index.column() == 1:
value = node.nodeValue()
if value is None:
return ''
else:
return ' '.join(node.nodeValue().split('\n'))
return None
def flags(self, index):
if not index.isValid():
return Qt.NoItemFlags
return Qt.ItemIsEnabled | Qt.ItemIsSelectable
def headerData(self, section, orientation, role):
if orientation == Qt.Horizontal and role == Qt.DisplayRole:
if section == 0:
return "Category"
if section == 1:
return "Name"
return None
def index(self, row, column, parent):
if not self.hasIndex(row, column, parent):
return QModelIndex()
if not parent.isValid():
parentItem = self.rootItem
else:
parentItem = parent.internalPointer()
childItem = parentItem.child(row)
if childItem:
return self.createIndex(row, column, childItem)
else:
return QModelIndex()
def parent(self, child):
if not child.isValid():
return QModelIndex()
childItem = child.internalPointer()
parentItem = childItem.parent()
if not parentItem or parentItem == self.rootItem:
return QModelIndex()
return self.createIndex(parentItem.row(), 0, parentItem)
def rowCount(self, parent):
if parent.column() > 0:
return 0
if not parent.isValid():
parentItem = self.rootItem
else:
parentItem = parent.internalPointer()
return parentItem.node().childNodes().count()
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.fileMenu = self.menuBar().addMenu("&File")
self.fileMenu.addAction("&Open...", self.openFile, "Ctrl+O")
self.fileMenu.addAction("E&xit", self.close, "Ctrl+Q")
self.xmlPath = ""
self.model = DomModel(QDomDocument(), self)
self.view = QTreeView(self)
self.view.setModel(self.model)
self.setCentralWidget(self.view)
self.setWindowTitle("Simple DOM Model")
def openFile(self, filePath):
if filePath:
f = QFile(filePath)
if f.open(QIODevice.ReadOnly):
document = QDomDocument()
if document.setContent(f):
newModel = DomModel(document, self)
self.view.setModel(newModel)
self.view.expandAll()
self.model = newModel
self.xmlPath = filePath
f.close()
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
window = MainWindow()
window.resize(640, 480)
window.show()
window.openFile('your file full path')
sys.exit(app.exec_())
The xml file is:
<repoCoordinates>
<name>repostructuretrials</name>
<branches>master</branches>
<submodules>
<submodule_0>
<name>gameengine</name>
<branches>*master,remotes/origin/develop,remotes/origin/feature/NormalMappingAndTextureCombination,remotes/origin/feature/light,remotes/origin/feature/particleEmitter,remotes/origin/master</branches>
</submodule_0>
<submodule_1>
<name>GraphicEngineOpenGLBasics</name>
<branches>feature/shadows,*master,remotes/origin/develop,remotes/origin/feature/billboards,remotes/origin/feature/shadows,remotes/origin/master</branches>
</submodule_1>
</submodules>
</repoCoordinates>
The code is large but not very complex.
My problem is that I want to have each branch name as a diferent item in the tree model, and all the values separated by comas are the one unique item for the model. I tried parsing the xml, and played with the QDomElement()
class trying to append childs to my nodes to be able to choose each of the branches separatedly.
I would like to split all the branches names by the "," and have this nodes added in my DOM.
Any help on this would me much appreciated, as well as any advice to handle this if the QDomDocument
class is not the best way.
Thanks a lot
This is a very basic attempt that could achieve what you're looking for.
What it does is to create subchildren whenever the node detects its name is "branches", so that the item model can see them as actual children nodes. The children are manually created by writing their own index on the parent childItems
dict, so that the model can navigate between the parents.
Keep in mind that this approach is far from perfect: for instance, the model and the relative DOM is not write-safe (I've not that amount of experience with the QtXml module).
class DomItem(object):
# ...
def child(self, i):
if i in self.childItems:
return self.childItems[i]
if 0 <= i < self.domNode.childNodes().count():
childNode = self.domNode.childNodes().item(i)
childItem = DomItem(childNode, i, self)
self.childItems[i] = childItem
if childNode.nodeName() == 'branches':
# based on your example xml there should be only one child entry
# for each "branches" node, but, just in case, let's cycle amongst
# every possible child node
subChildNodes = childNode.childNodes()
childIndex = 0
for c in range(subChildNodes.count()):
subChild = subChildNodes.at(c)
for tag in subChild.nodeValue().split(','):
branch = childNode.ownerDocument().createElement(tag)
childNode.appendChild(branch)
branchItem = DomItem(branch, childIndex, childItem)
childItem.childItems[childIndex] = branchItem
childIndex += 1
return childItem
return None