web-dev-qa-db-fra.com

Comment rendre le rendu riche (html) de la vue d'élément dans Qt

Supposons que mon modèle comporte des éléments avec la chaîne suivante pour Qt :: DisplayRole

<span>blah-blah <b>some text</b> other blah</span>

Je veux que QTreeView (en fait, n'importe quelle vue d'élément) le rende comme un texte riche. Au lieu de cela, les vues d'élément le rendent comme un texte pur par défaut. Comment obtenir le rendu souhaité?


En fait, il s'agit d'un modèle de résultats de recherche. L'utilisateur entre un texte, certains documents sont recherchés par rapport à ce texte et l'utilisateur est présenté avec des résultats de recherche, où les mots recherchés doivent être plus audacieux que le texte environnant.

47
Anthony Serdyukov

Ma réponse est principalement inspirée de celle de @ serge_gubenko. Cependant, plusieurs améliorations ont été apportées afin que le code soit enfin utile dans mon application.

class HtmlDelegate : public QStyledItemDelegate
{
protected:
    void Paint ( QPainter * Painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const;
    QSize sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const;
};

void HtmlDelegate::Paint(QPainter *Painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    QStyleOptionViewItemV4 optionV4 = option;
    initStyleOption(&optionV4, index);

    QStyle *style = optionV4.widget? optionV4.widget->style() : QApplication::style();

    QTextDocument doc;
    doc.setHtml(optionV4.text);

    /// Painting item without text
    optionV4.text = QString();
    style->drawControl(QStyle::CE_ItemViewItem, &optionV4, Painter);

    QAbstractTextDocumentLayout::PaintContext ctx;

    // Highlighting text if item is selected
    if (optionV4.state & QStyle::State_Selected)
        ctx.palette.setColor(QPalette::Text, optionV4.palette.color(QPalette::Active, QPalette::HighlightedText));

    QRect textRect = style->subElementRect(QStyle::SE_ItemViewItemText, &optionV4);
    Painter->save();
    Painter->translate(textRect.topLeft());
    Painter->setClipRect(textRect.translated(-textRect.topLeft()));
    doc.documentLayout()->draw(Painter, ctx);
    Painter->restore();
}

QSize HtmlDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    QStyleOptionViewItemV4 optionV4 = option;
    initStyleOption(&optionV4, index);

    QTextDocument doc;
    doc.setHtml(optionV4.text);
    doc.setTextWidth(optionV4.rect.width());
    return QSize(doc.idealWidth(), doc.size().height());
}
21
Anthony Serdyukov

Je suppose que vous pouvez utiliser la méthode setItemDelegate de l'arborescence pour configurer Painter personnalisé pour vos éléments d'arborescence. Dans la méthode Paint du délégué, vous pouvez utiliser QTextDocument pour charger le texte de l'élément en html et le rendre. Veuillez vérifier si un exemple ci-dessous vous conviendrait:

initialisation de l'arborescence:

...
    // create simple model for a tree view
    QStandardItemModel *model = new QStandardItemModel();
    QModelIndex parentItem;
    for (int i = 0; i < 4; ++i)
    {
        parentItem = model->index(0, 0, parentItem);
        model->insertRows(0, 1, parentItem);
        model->insertColumns(0, 1, parentItem);
        QModelIndex index = model->index(0, 0, parentItem);
        model->setData(index, "<span>blah-blah <b>some text</b> other blah</span>");
    }
    // create custom delegate
    HTMLDelegate* delegate = new HTMLDelegate();
    // set model and delegate to the treeview object
    ui->treeView->setModel(model);
    ui->treeView->setItemDelegate(delegate);
...

implémentation de délégué personnalisé

class HTMLDelegate : public QStyledItemDelegate
{
protected:
    void Paint ( QPainter * Painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const;
    QSize sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const;
};

void HTMLDelegate::Paint(QPainter* Painter, const QStyleOptionViewItem & option, const QModelIndex &index) const
{
    QStyleOptionViewItemV4 options = option;
    initStyleOption(&options, index);

    Painter->save();

    QTextDocument doc;
    doc.setHtml(options.text);

    options.text = "";
    options.widget->style()->drawControl(QStyle::CE_ItemViewItem, &options, Painter);

    Painter->translate(options.rect.left(), options.rect.top());
    QRect clip(0, 0, options.rect.width(), options.rect.height());
    doc.drawContents(Painter, clip);

    Painter->restore();
}

QSize HTMLDelegate::sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const
{
    QStyleOptionViewItemV4 options = option;
    initStyleOption(&options, index);

    QTextDocument doc;
    doc.setHtml(options.text);
    doc.setTextWidth(options.rect.width());
    return QSize(doc.idealWidth(), doc.size().height());
}

j'espère que cela vous aide,

update0 : modifications de HTMLDelegate pour rendre les icônes visibles et différentes couleurs de stylet pour les éléments sélectionnés

void HTMLDelegate::Paint(QPainter* Painter, const QStyleOptionViewItem & option, const QModelIndex &index) const
{
    QStyleOptionViewItemV4 options = option;
    initStyleOption(&options, index);

    Painter->save();

    QTextDocument doc;
    doc.setHtml(options.text);

    options.text = "";
    options.widget->style()->drawControl(QStyle::CE_ItemViewItem, &options, Painter);

    // shift text right to make icon visible
    QSize iconSize = options.icon.actualSize(options.rect.size());
    Painter->translate(options.rect.left()+iconSize.width(), options.rect.top());
    QRect clip(0, 0, options.rect.width()+iconSize.width(), options.rect.height());

    //doc.drawContents(Painter, clip);

    Painter->setClipRect(clip);
    QAbstractTextDocumentLayout::PaintContext ctx;
    // set text color to red for selected item
    if (option.state & QStyle::State_Selected)
        ctx.palette.setColor(QPalette::Text, QColor("red"));
    ctx.clip = clip;
    doc.documentLayout()->draw(Painter, ctx);

    Painter->restore();
}
36
serge_gubenko

Voici la conversion PyQt de la combinaison des réponses ci-dessus qui a fonctionné pour moi. Je m'attendrais à ce que cela fonctionne pratiquement de la même manière pour PySide.

from PyQt4 import QtCore, QtGui

class HTMLDelegate(QtGui.QStyledItemDelegate):
    def Paint(self, Painter, option, index):
        options = QtGui.QStyleOptionViewItemV4(option)
        self.initStyleOption(options,index)

        style = QtGui.QApplication.style() if options.widget is None else options.widget.style()

        doc = QtGui.QTextDocument()
        doc.setHtml(options.text)

        options.text = ""
        style.drawControl(QtGui.QStyle.CE_ItemViewItem, options, Painter);

        ctx = QtGui.QAbstractTextDocumentLayout.PaintContext()

        # Highlighting text if item is selected
        #if (optionV4.state & QStyle::State_Selected)
            #ctx.palette.setColor(QPalette::Text, optionV4.palette.color(QPalette::Active, QPalette::HighlightedText));

        textRect = style.subElementRect(QtGui.QStyle.SE_ItemViewItemText, options)
        Painter.save()
        Painter.translate(textRect.topLeft())
        Painter.setClipRect(textRect.translated(-textRect.topLeft()))
        doc.documentLayout().draw(Painter, ctx)

        Painter.restore()

    def sizeHint(self, option, index):
        options = QtGui.QStyleOptionViewItemV4(option)
        self.initStyleOption(options,index)

        doc = QtGui.QTextDocument()
        doc.setHtml(options.text)
        doc.setTextWidth(options.rect.width())
        return QtCore.QSize(doc.idealWidth(), doc.size().height())
15
jbmohler

Celui-ci est dans PySide. Plutôt que de faire beaucoup de dessins personnalisés, je passe le QPainter au QLabel et le fais dessiner lui-même. Code de mise en évidence emprunté à d'autres réponses.

from PySide import QtGui

class TaskDelegate(QtGui.QItemDelegate):
    #https://doc.qt.io/archives/qt-4.7/qitemdelegate.html#drawDisplay
    #https://doc.qt.io/archives/qt-4.7/qwidget.html#render
    def drawDisplay(self, Painter, option, rect, text):
        label = QtGui.QLabel(text)

        if option.state & QtGui.QStyle.State_Selected:
            p = option.palette
            p.setColor(QtGui.QPalette.WindowText, p.color(QtGui.QPalette.Active, QtGui.QPalette.HighlightedText))

            label.setPalette(p)

        label.render(Painter, rect.topLeft(), renderFlags=QtGui.QWidget.DrawChildren)
5
Pepijn