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 class RSpecView(QTreeView): def __init__(self, parent): QTreeView.__init__(self, parent) self.setAnimated(True) self.setItemsExpandable(True) self.setRootIsDecorated(True) self.setHeaderHidden(True) self.setAttribute(Qt.WA_MacShowFocusRect, 0) self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) def expandMatchingText(self, txt): self.collapseAll() self.expandToDepth(0) model = self.model() def recursiveExpand(index): parent = index.parent() if parent and parent.isValid(): recursiveExpand(parent) self.expand(index) def search(index): if index.data().toString() == txt: recursiveExpand(index) self.scrollTo(index, self.PositionAtCenter) return rows = model.rowCount(index) for r in range(rows): child_index = index.child(r, 0) search(child_index) root_rows = model.rowCount() for r in range(root_rows): index = model.index(r, 0) search(index) 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): # sometimes it return a QString, sometimes a QVariant. not good. 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) return QString('<%s%s>'% (node.nodeName(), qslist.join(' '))) elif node.nodeType() == QDomNode.AttributeNode: return QString('Whozat?!') elif node.nodeType() == QDomNode.TextNode: return node.nodeValue() elif node.nodeType() == QDomNode.CDATASectionNode: return QString('unsupported node type') elif node.nodeType() == QDomNode.EntityReferenceNode: return QString('unsupported node type') elif node.nodeType() == QDomNode.EntityNode: return QString('unsupported node type') elif node.nodeType() == QDomNode.ProcessingInstructionNode: return QVariant() #return node.nodeName() elif node.nodeType() == QDomNode.CommentNode: return QString('#').append(node.nodeValue()) elif node.nodeType() == QDomNode.DocumentNode: return QString('unsupported node type') elif node.nodeType() == QDomNode.DocumentTypeNode: return QString('unsupported node type') elif node.nodeType() == QDomNode.DocumentFragmentNode: return QString('unsupported node type') elif node.nodeType() == QDomNode.NotationNode: return QString('unsupported node type') elif node.nodeType() == QDomNode.BaseNode: return QString('unsupported node type') elif node.nodeType() == QDomNode.CharacterDataNode: return QString('unsupported node type') else: return QVariant() else: 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 RSpecWindow(QDialog): def __init__(self, parent=None): QDialog.__init__(self, parent) self.setWindowTitle("RSpec View") self.document = None self.model = None self.view = RSpecView(self) self.delegate = RSpecDelegate(self) self.view.setItemDelegate(self.delegate) layout = QVBoxLayout() layout.addWidget(self.view) self.setLayout(layout) self.updateView() def show(self): self.updateView() QDialog.show(self) def showNode(self, hostname): self.view.expandMatchingText(hostname) def updateView(self): del self.document del self.model self.document = None self.model = None rspec_file = config.getSliceRSpecFile() if not os.path.exists(rspec_file): return self.document = QDomDocument("RSpec") self.document.setContent(open(rspec_file,'r').read()) self.model = DomModel(self.document, self) self.view.setModel(self.model) self.view.expand(self.model.index(0, 0)) #expand first level only class RSpecDelegate(QAbstractItemDelegate): def __init__(self, parent=None): print "init-ing the delegate" QAbstractItemDelegate.__init__(self, parent) def paint(self, painter, option, index): text = index.model().data(index) 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)