X-Git-Url: http://git.onelab.eu/?p=sface.git;a=blobdiff_plain;f=sface%2Fxmlwidget.py;h=13c26e0c9d866df65ec790ca646f1dab779d7b56;hp=512d35a1efb0f1e4239577c309f4bc92a167159d;hb=77e243a9b79137aadeaaf60acc7e1da03a75f8ab;hpb=6ba5d90f9b2b0ba25aeb46d0d92f22b1f8d33e90 diff --git a/sface/xmlwidget.py b/sface/xmlwidget.py index 512d35a..13c26e0 100644 --- a/sface/xmlwidget.py +++ b/sface/xmlwidget.py @@ -1,4 +1,5 @@ import os +import shlex import sys from PyQt4.QtCore import * @@ -12,14 +13,6 @@ 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) - break self.rootItem = DomItem(document, 0); def data(self, index, role = Qt.DisplayRole): @@ -39,7 +32,7 @@ class DomModel(QAbstractItemModel): attr = attributeMap.item(i) elem = ' %s="%s"' % (attr.nodeName(), attr.nodeValue()) qslist.append(elem) - ElemNameAndAtts = QString('<%s%s>'% (node.nodeName(), qslist.join(' '))) + ElemNameAndAtts = '%s%s'% (node.nodeName(), qslist.join(' ')) obj = QObject() obj.setProperty('nodeType', QString('element')) obj.setProperty('content', ElemNameAndAtts) @@ -58,7 +51,10 @@ class DomModel(QAbstractItemModel): elif node.nodeType() == QDomNode.EntityNode: return QString('unsupported node type') elif node.nodeType() == QDomNode.ProcessingInstructionNode: - return QVariant() + obj = QObject() + obj.setProperty('nodeType', QString('element')) + obj.setProperty('content', node.nodeName() + " " + node.nodeValue()) + return obj elif node.nodeType() == QDomNode.CommentNode: obj = QObject() obj.setProperty('nodeType', QString('comment')) @@ -85,7 +81,7 @@ class DomModel(QAbstractItemModel): if not index.isValid(): return Qt.ItemIsEnabled return Qt.ItemIsEnabled | Qt.ItemIsSelectable - + def headerData(self, section, orientation, role): return QVariant() @@ -146,7 +142,7 @@ class DomItem: self.childItems[i] = childItem return childItem return None - + def parent(self): return self.parentItem @@ -166,6 +162,7 @@ class XmlView(QTreeView): self.setHeaderHidden(True) self.setAttribute(Qt.WA_MacShowFocusRect, 0) self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) + self.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel) class XmlWindow(QDialog): def __init__(self, parent=None, title='XML Window'): @@ -176,7 +173,7 @@ class XmlWindow(QDialog): self.model = None self.title = title - self.view = XmlView(self) + self.view = self.initView() self.delegate = XmlDelegate(self) self.view.setItemDelegate(self.delegate) self.delegate.insertNodeDelegate('element', ElemNodeDelegate()) @@ -186,12 +183,19 @@ class XmlWindow(QDialog): layout.addWidget(self.view) self.setLayout(layout) + self.connect(self.view, SIGNAL('expanded(QModelIndex)'), self.onItemExpanded) self.updateView() + def initView(self): + return XmlView(self) + def show(self): self.updateView() QDialog.show(self) - + + def readContent(self): + raise ValueError("readContent needs to be implemented in the subclass") + def updateView(self): del self.document del self.model @@ -202,16 +206,27 @@ class XmlWindow(QDialog): self.model = DomModel(self.document, self) self.view.setModel(self.model) - self.view.expand(self.model.index(0, 0)) #expand first level only - #move the code below to rspec window - rspec_file = config.getSliceRSpecFile() - if not os.path.exists(rspec_file): - return + self.document.setContent(self.readContent()) - self.document.setContent(open(rspec_file,'r').read()) + if self.document.childNodes().count() == 0: + # empty document - do nothing + pass + elif self.document.childNodes().item(0).nodeType() == QDomNode.ProcessingInstructionNode: + # the first item is the tag, so expand the second + self.view.expand(self.model.index(1,0)) + else: + # the document didn't start with an tag; let's try to expand + # the first item on the assumption that it is our root level xml + # tag. + self.view.expand(self.model.index(0,0)) + self.view.header().resizeSection(0,0) + self.view.resizeColumnToContents(0) + def onItemExpanded(self, index): + self.view.header().resizeSection(0,0) + self.view.resizeColumnToContents(0) class XmlDelegate(QItemDelegate): @@ -228,78 +243,150 @@ class XmlDelegate(QItemDelegate): del self.delegates[nodeType] def paint(self, painter, option, index): + if isinstance(index.model().data(index),QVariant): + return nodeType = index.model().data(index).property('nodeType') delegate = self.delegates.get(str(nodeType.toString())) - print "TYPE:", str(type(str(nodeType.toString()))) - print "DELEGS DICT:", self.delegates - print "NODETYPE:", nodeType.toString() + #print "TYPE:", str(type(str(nodeType.toString()))) + #print "DELEGS DICT:", self.delegates + #print "NODETYPE:", nodeType.toString() if delegate is not None: - print "WOW DELEG ISNT NONE" + #print "WOW DELEG ISNT NONE" delegate.paint(painter, option, index) else: - print "ELSE BRANCH" + #print "ELSE BRANCH" # not sure this will ever work. this delegate # doesn't know about my QObject strategy. QItemDelegate.paint(self, painter, option, index) + def sizeHint(self, option, index): + fm = option.fontMetrics + if isinstance(index.model().data(index),QVariant): + return QSize(0, 0) + + nodeType = index.model().data(index).property('nodeType') + delegate = self.delegates.get(str(nodeType.toString()), None) + + # Use the text from the appropriate delegate if we have it, to + # compute the size. Should probably finish the sizeHint() methods + # of the delegates at some point, and call them instead. + if delegate!=None and hasattr(delegate, "getItemText"): + text = delegate.getItemText(option, index) + else: + text = index.model().data(index).property('content').toString() + + 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) + class ElemNodeDelegate(QAbstractItemDelegate): - def paint(self, painter, option, index): - print "ELEM DELEG CALLED" - paint(self, painter, option, index) + def getItemText(self, option, index): + text = index.model().data(index) + nonHighGlobPattern = '<%s%s>' + nonHighAttPattern = ' %s="%s"' + highGlobPattern = '<%s%s>' + highAttPattern = ' %s="%s"' + + def getHtmlText(plainText, globPattern, attPattern): +# print "PLAIN TEXT:", plainText + tmp = plainText.split(' ', 1) +# print "TMP:", tmp + elemName = tmp[0] + AttListHtml = '' + if len(tmp) > 1: + # many elems don't have atts... + # use shlex.split so we can handle quoted strings with spaces + # in them, like . Note that there are + # documented problems with shlex.split and unicode, so we + # convert any potential unicode to a string first. + attList = shlex.split(str(tmp[1])) + for att in attList: + tmp = att.split('=',1) + if len(tmp)>=2: + attName = tmp[0] + attValue = tmp[1] + else: + # this shouldn't happen, but if it does, pretend the + # attribute value is blank. + attName = tmp[0] + attValue = "" + AttListHtml += (nonHighAttPattern % (attName, attValue)) + html = (globPattern % (elemName, AttListHtml)) + return html + + def colorize(color, text): + return '' + text + '' + + text = str(index.model().data(index).property('content').toString()) +# print "TEXT:", text + if option.state & QStyle.State_Selected: + palette = QApplication.palette() + htmlText = colorize(palette.highlightedText().color().name(), + getHtmlText(text, highGlobPattern, highAttPattern)) + else: + htmlText = getHtmlText(text, nonHighGlobPattern, nonHighAttPattern) - def sizeHint(self, option, index): - sizeHint(self, option, index) + return htmlText + + def paint(self, painter, option, index): + palette = QApplication.palette() + document = QTextDocument() + document.setDefaultFont(option.font) + + htmlText = self.getItemText(option, index) + document.setHtml(QString(htmlText)) + + color = palette.highlight().color() \ + if option.state & QStyle.State_Selected \ + else palette.base().color() + + painter.save() +# print "COLOR:", color.name() + # 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): - print "TEXT DELEG CALLED" + #print "TEXT DELEG CALLED" paint(self, painter, option, index) - def sizeHint(self, option, index): - sizeHint(self, option, index) - class CommentNodeDelegate(QAbstractItemDelegate): def paint(self, painter, option, index): - print "TEXT DELEG CALLED" + #print "TEXT DELEG CALLED" paint(self, painter, option, index) - def sizeHint(self, option, index): - sizeHint(self, 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() -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() - -def sizeHint(self, option, index): - fm = option.fontMetrics - text = index.model().data(index) - 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)