PyQt listview with html rich text delegate moves t

2019-02-26 08:36发布

gif showing with delegate and without

Got a listview where items need to make use of bold text, so a html delegate is the way to go. I looked around and found several solutions and out of them I made this code, but it has the issue that you can see in the gif, which shows the results with the use of the delegate and without.

from PyQt4.QtCore import *
from PyQt4.QtGui import *
import sys

class HTMLDelegate(QStyledItemDelegate):
    def __init__(self, parent=None):
        super(HTMLDelegate, self).__init__(parent)
        self.doc = QTextDocument(self)

    def paint(self, painter, option, index):
        painter.save()

        options = QStyleOptionViewItemV4(option)
        self.initStyleOption(options, index)

        self.doc.setHtml(options.text)
        options.text = ""

        style = QApplication.style() if options.widget is None \
            else options.widget.style()
        style.drawControl(QStyle.CE_ItemViewItem, options, painter)

        ctx = QAbstractTextDocumentLayout.PaintContext()

        if option.state & QStyle.State_Selected:
            ctx.palette.setColor(QPalette.Text, option.palette.color(
                                 QPalette.Active, QPalette.HighlightedText))

        textRect = style.subElementRect(QStyle.SE_ItemViewItemText, options)
        painter.translate(textRect.topLeft())
        self.doc.documentLayout().draw(painter, ctx)

        painter.restore()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    data = ['1','2','3','4','5','6','7','8','9']
    main_list = QListView()
    main_list.setItemDelegate(HTMLDelegate())
    main_list.setModel(QStringListModel(data))
    main_list.show()
    sys.exit(app.exec_())

Here are PyQt5 and PySide versions

I dont use sizeHint, since through my testing it didnt feel like it was affecting this behavior. Some attribute is positioning the text wrongly, I have been able to partly counter it with:

self.doc.setDocumentMargin(0)

the default margin is 4, and then move the whole item bit to the right

rect = options.rect
options.rect = QRect(rect.x()+3, rect.y(), rect.width(), rect.height())

But I dont think it addresses the cause of this issue, it just masks it and it becomes a problem again once you try add icons for example

2条回答
啃猪蹄的小仙女
2楼-- · 2019-02-26 09:08

Well after some more digging and trying, adjusting the text rectangle seems to give the feel that everything is allright and seems to behave consistently in various DEs

    textRect.adjust(-1, -4, 0, 0)

I am not sure if its a bug, but one would expect that SE_ItemViewItemText should have give the correct position on its own

查看更多
Root(大扎)
3楼-- · 2019-02-26 09:16

this one is now the one I use

from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
import sys

class HTMLDelegate(QStyledItemDelegate):
    def __init__(self, parent=None):
        super().__init__()
        self.doc = QTextDocument(self)

    def paint(self, painter, option, index):
        painter.save()

        options = QStyleOptionViewItem(option)

        self.initStyleOption(options, index)
        self.doc.setHtml(options.text)
        options.text = ""

        style = QApplication.style() if options.widget is None \
            else options.widget.style()
        style.drawControl(QStyle.CE_ItemViewItem, options, painter)

        ctx = QAbstractTextDocumentLayout.PaintContext()

        print(QStyle.State_Selected)

        if option.state & QStyle.State_Selected:
            ctx.palette.setColor(QPalette.Text, option.palette.color(
                QPalette.Active, QPalette.HighlightedText))
        else:
            ctx.palette.setColor(QPalette.Text, option.palette.color(
                QPalette.Active, QPalette.Text))

        textRect = style.subElementRect(
            QStyle.SE_ItemViewItemText, options)

        if index.column() != 0:
            textRect.adjust(5, 0, 0, 0)

        thefuckyourshitup_constant = 4
        margin = (option.rect.height() - options.fontMetrics.height()) // 2
        margin = margin - thefuckyourshitup_constant
        textRect.setTop(textRect.top() + margin)

        painter.translate(textRect.topLeft())
        painter.setClipRect(textRect.translated(-textRect.topLeft()))
        self.doc.documentLayout().draw(painter, ctx)

        painter.restore()

    def sizeHint(self, option, index):
        return QSize(self.doc.idealWidth(), self.doc.size().height())

if __name__ == '__main__':
    app = QApplication(sys.argv)
    data = ['1','2','3','4','5','6','7','8','9']
    main_list = QListView()
    main_list.setItemDelegate(HTMLDelegate())
    main_list.setModel(QStringListModel(data))
    main_list.show()
    sys.exit(app.exec_())
查看更多
登录 后发表回答