在 QTreeView (Qt/PySide/PyQt) 中实现自动换行的委托?

Implementing a delegate for wordwrap in a QTreeView (Qt/PySide/PyQt)?

我有一个带有自定义委托的树视图,我正在尝试向其中添加自动换行功能。自动换行工作正常,但 sizeHint() 似乎不起作用,因此当文本换行时,相关行不会扩展以包含它。

我以为我在 sizeHint() 通过返回 document.size().height() 处理了它。

def sizeHint(self, option, index):
    text = index.model().data(index)
    document = QtGui.QTextDocument()
    document.setHtml(text) 
    document.setTextWidth(option.rect.width())  
    return QtCore.QSize(document.idealWidth(), document.size().height())    

但是,当我打印出来时 document.size().height() 每一项都是一样的。

此外,即使我手动设置高度(比如 75)只是为了检查事情看起来是否合理,这棵树看起来就像一条被火箭筒射中的金鱼(也就是说,一团糟):

如您所见,每行中的文本在树中未正确对齐。

类似帖子

以前也出现过类似的问题,但没有解决我的问题(人们通常说重新实现 sizeHint(),这就是我正在尝试的):

QTreeWidget set height of each row depending on content

http://www.qtcentre.org/threads/1289-QT4-QTreeView-and-rows-with-multiple-lines

SSCCE

import sys
from PySide import QtGui, QtCore

class SimpleTree(QtGui.QTreeView):
    def __init__(self, parent = None):    
        QtGui.QTreeView.__init__(self, parent)
        self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
        self.setGeometry(500,200, 400, 300)  
        self.setUniformRowHeights(False) #optimize: but for word wrap, we don't want this!
        print "uniform heights in tree?", self.uniformRowHeights()
        self.model = QtGui.QStandardItemModel()
        self.model.setHorizontalHeaderLabels(['Task', 'Description'])
        self.setModel(self.model)
        self.rootItem = self.model.invisibleRootItem()
        item0 = [QtGui.QStandardItem('Sneeze'), QtGui.QStandardItem('You have been blocked up')]
        item00 = [QtGui.QStandardItem('Tickle nose, this is a very long entry. Row should resize.'), QtGui.QStandardItem('Key first step')]
        item1 = [QtGui.QStandardItem('<b>Get a job</b>'), QtGui.QStandardItem('Do not blow it')]
        self.rootItem.appendRow(item0)
        item0[0].appendRow(item00) 
        self.rootItem.appendRow(item1)
        self.setColumnWidth(0,150)
        self.expandAll()
        self.setWordWrap(True)
        self.setItemDelegate(ItemWordWrap(self))

class ItemWordWrap(QtGui.QStyledItemDelegate):
    def __init__(self, parent=None):
        QtGui.QStyledItemDelegate.__init__(self, parent)
        self.parent = parent
    def paint(self, painter, option, index):
        text = index.model().data(index) 
        document = QtGui.QTextDocument() # #print "dir(document)", dir(document)
        document.setHtml(text)       
        document.setTextWidth(option.rect.width())  #keeps text from spilling over into adjacent rect
        painter.save() 
        painter.translate(option.rect.x(), option.rect.y()) 
        document.drawContents(painter)  #draw the document with the painter
        painter.restore()
    def sizeHint(self, option, index):
        #Size should depend on number of lines wrapped
        text = index.model().data(index)
        document = QtGui.QTextDocument()
        document.setHtml(text) 
        document.setTextWidth(option.rect.width())  
        return QtCore.QSize(document.idealWidth() + 10,  document.size().height())       

def main():
    app = QtGui.QApplication(sys.argv)
    myTree = SimpleTree()
    myTree.show()
    sys.exit(app.exec_())
    
if __name__ == "__main__":
    main()

这个问题似乎源于这样一个事实,即 option.rect.width() 传递给 QStyledItemDelegate.sizeHint() 的值是 -1。这明显是假的!

我通过 paint() 方法在模型中存储宽度并从 sizeHint() 访问它来解决这个问题。

因此在您的 paint() 方法中添加以下行:

index.model().setData(index, option.rect.width(), QtCore.Qt.UserRole+1)

并在您的 sizeHint() 方法中,将 document.setTextWidth(option.rect.width()) 替换为:

width = index.model().data(index, QtCore.Qt.UserRole+1)
if not width:
    width = 20
document.setTextWidth(width)