2009-12-24 77 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

请注意,如果optionV4.state的帐户处于非活动状态,则ctx.palette.setcolor部分需要额外的嵌套。否则,当你移动到另一个窗口时,文本变得几乎不可读。否则工作很好。谢谢 – mmccoo 2010-12-30 14:41:32

+3

文字颜色注意:使用'else ctx.palette.setColor(QPalette :: Text,optionV4.palette.color(QPalette :: Active,QPalette :: Text));'以确保正确设置文字颜色。通过样式表使用非默认文本颜色时需要。 – 2014-05-16 13:52:46

+2

QTextDocument设置:如果添加'doc.setDocumentMargin(0); doc.setDefaultFont(optionV4.font);'(将其添加到paint&sizeHint中),那么当您通过样式表更改字体时,字体将是正确的。此外,sizeHint例程中的'doc.setTextWidth'调用似乎没有任何作用。如果将它放在'sizeHint'和'paint'方法中,那么当单词的列缩小时,可以让单词消失而不是被截断。 – 2014-05-16 13:56:23

34

我想你可以使用树形视图的setItemDelegate方法来为你的树视图项目设置自定义画家。在委托的绘制方法中,您可以使用QTextDocument将项目的文本加载为html并进行渲染。请检查下面的例子会为你工作:

树状初始化:

... 
    // 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.所选项目应具有其他文字颜色。 试图弄清楚如何解决它们。 – 2009-12-24 08:16:38

+0

请检查update0的原始帖子; HTMLDelegate :: paint方法中有更改。为了使图标可见,我只是将文本右移到图标的宽度。至于文字颜色,我不得不改变颜料上下文对象的文本颜色的调色板设置。希望这是你要找的东西,关于 – 2009-12-24 21:47:17

+0

@Anton你知道如何修改选定的文本颜色吗? – To1ne 2012-10-26 11:59:20

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

什么黑客!呃,但是,谢谢。突出显示:如果options.state&QtGui.QStyle.State_Selected: ctx.palette.setColor(QtGui.QPalette.Text,options.palette.color(QtGui.QPalette.Active,QtGui.QPalette.HighlightedText)) – Pepijn 2011-11-06 21:44:31

+3

After line:' doc.setHtml(options.text)',你还需要设置'doc.setTextWidth(option.rect.width())',否则委托不会正确地渲染与目标绘图区相关的更长的内容。例如,不会在QListView中包装单词。 – Timo 2012-02-25 15:45:44

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 2012-04-06 23:36:17

+0

对于那些需要它的人:我修改了@Pepijn的答案,以覆盖http://stackoverflow.com/a/38028318/1504082中的多行标签 – maggie 2016-06-25 12:04:02