2009-12-24 5 views
40

Предположит, моя модель имеет элементы со следующей строкой для Qt :: DisplayRoleКак сделать вид элемента визуализация богатый (HTML) текстом в Qt

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

Я хочу QTreeView (на самом деле, любой вид элемента), чтобы сделать его как богатый текст. Вместо этого представления элементов отображают его как чистый текст по умолчанию. Как добиться желаемого рендеринга?


На самом деле, это модель результатов поиска. Пользователь вводит текст, какой-то документ просматривается в этом тексте, а пользователю представлены результаты поиска, где поиск слов должен быть смелее, чем окружающий текст.

ответ

19

Мой ответ в основном вдохновлен символом @ serge_gubenko. Тем не менее, было сделано несколько улучшений, поэтому код, наконец, полезен в моем приложении.

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

Обратите внимание, что раздел ctx.palette.setcolor нуждается в дополнительном вложенном, если учетная запись для опцииV4.state неактивна.В противном случае при переходе в другое окно текст становится почти нечитаемым. Отлично работает. Спасибо – mmccoo

+3

Замечание цвета текста: используйте 'else ctx.palette.setColor (QPalette :: Text, optionV4.palette.color (QPalette :: Active, QPalette :: Text));' чтобы убедиться, что цвет текста правильно установлен. Требуется при использовании цветов текста, отличных от по умолчанию, через таблицу стилей. –

+2

QTextDocument setup: Если вы добавите 'doc.setDocumentMargin (0); doc.setDefaultFont (optionV4.font); '(добавьте его как в paint & sizeHint), тогда шрифты будут правильными, когда вы измените их с помощью таблицы стилей. Кроме того, вызов 'doc.setTextWidth' в процедуре sizeHint, похоже, ничего не делает. Если вы поместите его как в методы 'sizeHint', так и' paint', вы можете потерять слова вместо обрезания, когда столбец элемента сжимается. –

34

Я думаю, вы можете использовать метод древовидной структуры setItemDelegate, чтобы настроить пользовательский маляр для ваших элементов дерева. В методе рисования делегата вы можете использовать QTextDocument для загрузки текста элемента в виде html и его рендеринга. Пожалуйста, проверьте, если ниже пример будет работать для вас:

инициализации TreeView:

... 
    // 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); 
... 

реализация пользовательских делегат

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

надеюсь, что это помогает, считает

update0: изменения HTMLDelegate, чтобы сделать иконки видимыми и разными цветами пера для выбранных элементов.

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

Спасибо за ваш ответ. Фактически, я играл с переопределяющим делегатом и QTextDocument. Однако возникла проблема с размером элементов. Ваш ответ указал мне на 'initStyleOption' и' widget-> style() -> drawControl'. Ваше решение отлично, за исключением двух проблем. 1. Текст нарисован над значком элемента 2. Выбранный элемент должен иметь другой цвет текста. Попытка выяснить, как их исправить. –

+0

PLS проверить update0 для исходного сообщения; изменения находятся в методе HTMLDelegate :: paint. Чтобы сделать иконки видимыми, я просто переместил текст прямо на ширину значка. Что касается цвета текста, мне пришлось изменить параметры палитры для цвета текста объекта контекста рисования. Надеюсь, это то, что вы ищете, считает –

+0

@ Антон вы выяснили, как изменить выделенный цвет текста? – To1ne

15

Здесь приведено преобразование PyQt из сочетания приведенных выше ответов, которые работали для меня. Я ожидаю, что это будет работать практически одинаково для 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()) 
+1

Какой взлом! Фу, но спасибо. Выделение: if options.state & QtGui.QStyle.State_Selected: ctx.palette.setColor (QtGui.QPalette.Text, options.palette.color (QtGui.QPalette.Active, QtGui.QPalette.HighlightedText)) – Pepijn

+3

После строки: ' doc.setHtml (options.text) ', вам также нужно установить' doc.setTextWidth (option.rect.width()) ', иначе делегат не будет корректно отображать более длинный контент в отношении целевой области рисования. Например, не завершает слова в QListView. – Timo

4

Этот номер находится в PySide. Вместо того, чтобы делать много пользовательского чертежа, я передаю QPainter в QLabel и заставляю его рисовать сам. Выделение кода, заимствованного из других ответов.

from PySide import QtGui 

class TaskDelegate(QtGui.QItemDelegate): 
    #http://doc.qt.nokia.com/4.7/qitemdelegate.html#drawDisplay 
    #http://doc.qt.nokia.com/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) 
+1

Не работает для меня, я вижу только небольшую часть текста, случайным образом и одну запись. – WhyNotHugo

+0

Для тех, кто в этом нуждается: я изменил @Pepijn немного, чтобы также покрывать многострочные метки в http://stackoverflow.com/a/38028318/1504082 – maggie

 Смежные вопросы

  • Нет связанных вопросов^_^