import os import sys from PyQt4.QtCore import * from PyQt4.QtGui import * from PyQt4.QtXml import * from sface.config import config from sface.screens.sfascreen import SfaScreen def QVarMapAccess(qv, key): # helper function. qv is a dict wrapped into a QVariant print 10*'=' print "DICT:", qv.toMap() if len(qv.toMap().keys()) == 0: print "EMPTY!" import traceback traceback.print_stack() return None else: return qv.toMap()[QString(key)].toString() class DomModel(QAbstractItemModel): def __init__(self, document, parent = 0): QAbstractItemModel.__init__(self, parent) self.domDocument = document # one of the children of the rootItem is the 'xml' thing. # here I delete it. childList = document.childNodes() for i in range(childList.count()): currElem = childList.item(i) if (currElem.nodeType() == QDomNode.ProcessingInstructionNode): document.removeChild(currElem) print "REMOVED!" break self.rootItem = DomItem(document, 0); def data(self, index, role = Qt.DisplayRole): # for interesting nodes, returns a dict wrapped into a QVariant. if not index.isValid(): return QVariant() if role != Qt.DisplayRole: return QVariant() node = index.internalPointer().node() attributeMap = node.attributes() col = index.column() if col == 0: if node.nodeType() == QDomNode.ElementNode: qslist = QStringList() for i in range(attributeMap.count()): attr = attributeMap.item(i) elem = ' %s="%s"' % (attr.nodeName(), attr.nodeValue()) qslist.append(elem) ElemNameAndAtts = '%s%s'% (node.nodeName(), qslist.join(' ')) print "1" return QVariant( {'nodeType':QVariant(QString('element')), 'content':ElemNameAndAtts}) elif node.nodeType() == QDomNode.AttributeNode: print "2" return QVariant() elif node.nodeType() == QDomNode.TextNode: print "3" return QVariant( {'nodeType':QVariant(QString('text')), 'content':node.nodeValue()}) elif node.nodeType() == QDomNode.CDATASectionNode: print "4" return QString('unsupported node type') elif node.nodeType() == QDomNode.EntityReferenceNode: print "5" return QString('unsupported node type') elif node.nodeType() == QDomNode.EntityNode: print "6" return QString('unsupported node type') elif node.nodeType() == QDomNode.ProcessingInstructionNode: print "7" return QVariant() elif node.nodeType() == QDomNode.CommentNode: print "8" return QVariant( {'nodeType':QVariant(QString('comment')), 'content':node.nodeValue()}) elif node.nodeType() == QDomNode.DocumentNode: print "9" return QString('unsupported node type') elif node.nodeType() == QDomNode.DocumentTypeNode: print "10" return QString('unsupported node type') elif node.nodeType() == QDomNode.DocumentFragmentNode: print "12" return QString('unsupported node type') elif node.nodeType() == QDomNode.NotationNode: print "13" return QString('unsupported node type') elif node.nodeType() == QDomNode.BaseNode: print "14" return QString('unsupported node type') elif node.nodeType() == QDomNode.CharacterDataNode: print "15" return QString('unsupported node type') else: print "16" return QVariant() else: print "17" return QVariant() def flags(self, index): if not index.isValid(): return Qt.ItemIsEnabled return Qt.ItemIsEnabled | Qt.ItemIsSelectable def headerData(self, section, orientation, role): return QVariant() def index(self, row, column, parent=None): if not parent or not parent.isValid(): parentItem = self.rootItem else: parentItem = parent.internalPointer() childItem = parentItem.child(row) # childItem would be None to say "false"? 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=None): if not parent or not parent.isValid(): parentItem = self.rootItem else: parentItem = parent.internalPointer() return parentItem.node().childNodes().count() def columnCount(self, parent): # just one column we'll print tag name (and attributes) or the # tag content return 1 class DomItem: # wrapper around PyQt4.QtXml.QDomNode it keeps an hash of # childrens for performance reasons def __init__(self, node, row, parent = 0): # node is of type PyQt4.QtXml.QDomNode self.domNode = node self.parentItem = parent self.rowNumber = row self.childItems = {} def child(self, i): if i in self.childItems: return self.childItems[i] if i >= 0 and 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 parent(self): return self.parentItem def node(self): return self.domNode def row(self): return self.rowNumber class XmlView(QTreeView): def __init__(self, parent=None): QTreeView.__init__(self, parent) delegate = XmlDelegate(self) delegate.insertNodeDelegate('element', ElemNodeDelegate()) delegate.insertNodeDelegate('text', TextNodeDelegate()) delegate.insertNodeDelegate('comment', CommentNodeDelegate()) self.setItemDelegate(delegate) self.setAnimated(True) self.setItemsExpandable(True) self.setRootIsDecorated(True) self.setHeaderHidden(True) self.setAttribute(Qt.WA_MacShowFocusRect, 0) self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) class XmlDelegate(QItemDelegate): def __init__(self, parent=None): QAbstractItemDelegate.__init__(self, parent) self.delegates = {} def insertNodeDelegate(self, nodeType, delegate): delegate.setParent(self) self.delegates[nodeType] = delegate def removeNodeDelegate(self, nodeType, delegate): if nodeType in self.delegates: del self.delegates[nodeType] def paint(self, painter, option, index): print "ASKING FOR DATA" dataAsQVarMap = index.model().data(index) print "GOT DATA" nodeType = str(QVarMapAccess(dataAsQVarMap, 'nodeType')) delegate = self.delegates.get(nodeType) #print "DELEGS DICT:", self.delegates #print "NODETYPE:", nodeType.toString() if delegate is not None: #print "WOW DELEG ISNT NONE" delegate.paint(painter, option, index) else: #print "ELSE BRANCH" # not sure this will ever work. this delegate # doesn't know about my QMap strategy. QItemDelegate.paint(self, painter, option, index) # def sizeHint(self, option, index): # fm = option.fontMetrics # print "TYPE:", str(type(index.model().data(index).convert(QObject))) # text = "the fish doesn't talk" # #text = str(index.model().data(index).property('content').toString()) # nodeType = str(index.model().data(index).property('nodeType').toString()) # if nodeType == 'element' or nodeType == 'comment': # numlines = 1 # elif nodeType == 'text': # numlines = text.count('\n') # sys.__stdout__.write("TEXT: \n" + text) # else: # numlines = 1 # document = QTextDocument() # document.setDefaultFont(option.font) # document.setHtml(text) # # the +5 is for margin. The +4 is voodoo; # # fm.height just give it too small. # return QSize(document.idealWidth() + 5, (fm.height() + 4) * numlines) class ElemNodeDelegate(QAbstractItemDelegate): def paint(self, painter, option, index): palette = QApplication.palette() document = QTextDocument() document.setDefaultFont(option.font) nonHighGlobPattern = '<%s%s>' nonHighAttPattern = ' %s="%s"' highGlobPattern = '<%s%s>' highAttPattern = ' %s="%s"' def getHtmlText(plainText, globPattern, attPattern): tmp = plainText.split(' ', 1) elemName = tmp[0] AttListHtml = '' if len(tmp) > 1: # many elems don't have atts... attList = tmp[1].split() for att in attList: tmp = att.split('=') attName = tmp[0] attValue = tmp[1][1:-1] AttListHtml += (nonHighAttPattern % (attName, attValue)) html = (globPattern % (elemName, AttListHtml)) return html def colorize(color, text): return '' + text + '' dataAsQVarMap = index.model().data(index) text = str(QVarMapAccess(dataAsQVarMap, 'content')) if option.state & QStyle.State_Selected: htmlText = colorize(palette.highlightedText().color().name(), getHtmlText(text, highGlobPattern, highAttPattern)) document.setHtml(QString(htmlText)) else: htmlText = getHtmlText(text, nonHighGlobPattern, nonHighAttPattern) document.setHtml(QString(htmlText)) color = palette.highlight().color() \ if option.state & QStyle.State_Selected \ else palette.base().color() painter.save() # voodoo: if not highlighted, filling the rect # with the base color makes no difference painter.fillRect(option.rect, color) painter.translate(option.rect.x(), option.rect.y()) document.drawContents(painter) painter.restore() class TextNodeDelegate(QAbstractItemDelegate): def paint(self, painter, option, index): palette = QApplication.palette() document = QTextDocument() document.setDefaultFont(option.font) def verbatimize(text): text.replace('\n', '
') return '
' + text + '' + text + ''
        dataAsQVarMap = index.model().data(index)
        text = str(QVarMapAccess(dataAsQVarMap, 'content'))
        if option.state & QStyle.State_Selected:
            htmlText = colorize(palette.highlightedText().color().name(),
                                verbatimize(text))
            document.setHtml(QString(htmlText))
        else:
            htmlText = verbatimize(text)
            document.setHtml(QString(htmlText))
        color = palette.highlight().color() \
            if option.state & QStyle.State_Selected \
            else palette.base().color()
        painter.save()
        # voodoo: if not highlighted, filling the rect
        # with the base color makes no difference
        painter.fillRect(option.rect, color)
        painter.translate(option.rect.x(), option.rect.y())
        document.drawContents(painter)
        painter.restore()

class CommentNodeDelegate(QAbstractItemDelegate):
    def paint(self, painter, option, index): 
        paint(self, painter, option, index)

def paint(self, painter, option, index):
    text = index.model().data(index).property('content').toString()
    palette = QApplication.palette()
    document = QTextDocument()
    document.setDefaultFont(option.font)
    if option.state & QStyle.State_Selected:
        rx = QRegExp(QString(''))
        rx.setMinimal(True)
        # If selected, I remove the  by hand,
        # and give the highlight color
        document.setHtml(QString("%2") \
                             .arg(palette.highlightedText().color().name())\
                             .arg(text.replace(rx, QString('')).
                                  replace(QString(''),QString(''))))
    else:
        document.setHtml(text)
    color = palette.highlight().color() \
        if option.state & QStyle.State_Selected \
        else palette.base().color()
    painter.save()
    # voodoo: if not highlighted, filling the rect
    # with the base color makes no difference
    painter.fillRect(option.rect, color)
    painter.translate(option.rect.x(), option.rect.y())
    document.drawContents(painter)
    painter.restore()