在QTreeView(Qt/PySide/PyQt)中实现换行的委托怎么做?

3
我有一个带有自定义委托的树形视图,我正在尝试添加自动换行功能。自动换行正常工作,但是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),只是为了检查事情是否合理,树看起来像是被火箭筒击中的金鱼(也就是说,一团糟):

WTF

作为您所看到的,树中每行的文本未对齐。 类似的帖子 之前出现过类似的问题,但没有解决我的问题(人们通常说要重新实现sizeHint(),这正是我正在尝试的): QTreeWidget设置每行的高度,具体取决于内容 QTreeView自定义单个行的行高

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()
1个回答

6
问题似乎源于传递给QStyledItemDelegate.sizeHint()的option.rect.width()的值为-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)

哇,好棒的发现。我甚至没有考虑过它可能是错误的来源,现在看起来很漂亮。太棒了。 - eric
1
当我发现那个虚假的宽度时,我感到非常愤怒。在C++中,你不能在const感染的paint()方法内部执行setData(),所以我给委托类一个QTreeView成员变量,并执行了treeView->model()->setData( index, option.rect.width(), WIDTH_ROLE );。你也可以使用const_cast<QAbstractItemModel*>( index.model() )->setData(...) - remikz
我无法让这个工作起来。在PyQt6中,sizeHint()在paint()之前被调用。我最终实现了类似于@remikz的方法:我通过__init__()保留了对QTreeView的引用,并在sizeHint()中使用与视图宽度相关的宽度创建了QTextDocument,以便正确计算高度。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接