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.
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());
}
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();
}
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())
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)