如何更改 QListView 的自动换行模式?

how to change word wrap mode for QListView?

我创建了一个 QComboBox 并为其设置了自定义视图:

self.list_view = QListView()
self.list_view.setWordWrap(True)
self.list_view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

self.settings = QComboBox()
self.settings.setFixedHeight(28)
self.settings.setView(self.list_view)

如何为 QListViewQComboBox 设置特定的自动换行模式?

在文档中,我找到了一个 QTextOption class,它有一个 .setWrapMode(wrapmode: PySide6.QtGui.QTextOption.WrapMode) 方法,但没有找到为 [=] 设置 QTextOption 的方法12=]/QComboBox 就像 QListView 对应 QComboBox.

在维护模型数据的同时实现这一点的唯一方法是实现一个自定义项委托,该委托对两者都使用 QTextDocument sizeHint() and paint()

组合也必须是覆盖 showPopup(), which is required to update the default (assumed) width of the view and compute correct sizes. This step is fundamental, as before showing itself, the doesn't know yet if the scroll bars will be visible or not (which could depend on the maxVisibleItems() 属性 和屏幕高度的子类。

请注意,这种方法并不总是完美的:如果项目的文本很长并且组合很窄,即使模型的项目少于 maxVisibleItems,滚动条也可能会显示。作为一个小的变通办法,我确保画家总是剪裁到选项的内容。唯一的替代解决方案是根据当前组合宽度计算前十个项目的大小提示,然后检查这些提示的高度总和是否超过当前屏幕并最终在实际显示弹出窗口之前再次更改参考宽度.

from random import choice
from PyQt5 import QtGui, QtWidgets

letters = 'abcdefhgijklmnopqrstuvwxyz0123456789' * 2 + ' '

class WrapDelegate(QtWidgets.QStyledItemDelegate):
    referenceWidth = 0
    wrapMode = QtGui.QTextOption.WrapAtWordBoundaryOrAnywhere
    def sizeHint(self, opt, index):
        doc = QtGui.QTextDocument(index.data())
        textOption = QtGui.QTextOption()
        textOption.setWrapMode(self.wrapMode)
        doc.setDefaultTextOption(textOption)
        doc.setTextWidth(self.referenceWidth)
        return doc.size().toSize()

    def paint(self, qp, opt, index):
        self.initStyleOption(opt, index)
        style = opt.widget.style()
        opt.text = ''
        style.drawControl(style.CE_ItemViewItem, opt, qp, opt.widget)
        doc = QtGui.QTextDocument(index.data())
        textOption = QtGui.QTextOption()
        textOption.setWrapMode(textOption.WrapAtWordBoundaryOrAnywhere)
        doc.setDefaultTextOption(textOption)
        doc.setTextWidth(opt.rect.width())
        qp.save()
        qp.translate(opt.rect.topLeft())
        qp.setClipRect(0, 0, opt.rect.width(), opt.rect.height())
        doc.drawContents(qp)
        qp.restore()


class ComboWrap(QtWidgets.QComboBox):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.delegate = WrapDelegate(self.view())
        self.view().setItemDelegate(self.delegate)
        self.view().setResizeMode(QtWidgets.QListView.Adjust)

    def showPopup(self):
        container = self.view().parent()
        l, _, r, _ = container.layout().getContentsMargins()
        margin = l + r + container.frameWidth() * 2
        if self.model().rowCount() > self.maxVisibleItems():
            margin += self.view().verticalScrollBar().sizeHint().width()
        self.delegate.referenceWidth = self.width() - margin
        super().showPopup()


app = QtWidgets.QApplication([])
combo = ComboWrap()
combo.addItems([''.join(choice(letters) for i in range(100)) for i in range(20)])
combo.show()
app.exec()