如何使项目视图在Qt中呈现富(html)文本
假设我的模型中有Qt::DisplayRole具有以下字符串的项如何使项目视图在Qt中呈现富(html)文本,qt,rendering,richtext,qtreeview,qlistview,Qt,Rendering,Richtext,Qtreeview,Qlistview,假设我的模型中有Qt::DisplayRole具有以下字符串的项 <span>blah-blah <b>some text</b> other blah</span> 诸如此类的废话一些文字其他的废话 我希望QTreeView(实际上是任何项目视图)将其呈现为富文本。相反,默认情况下,项目视图将其呈现为纯文本。如何实现所需的渲染 实际上,这是一个搜索结果模型。用户输入文本,根据该文本搜索某些文档,并向用户显示搜索结果,其中搜索的单词应比周围的
<span>blah-blah <b>some text</b> other blah</span>
诸如此类的废话一些文字其他的废话
我希望QTreeView(实际上是任何项目视图)将其呈现为富文本。相反,默认情况下,项目视图将其呈现为纯文本。如何实现所需的渲染
实际上,这是一个搜索结果模型。用户输入文本,根据该文本搜索某些文档,并向用户显示搜索结果,其中搜索的单词应比周围的文本更粗体。我想您可以使用treeview的方法为treeview项目设置自定义画师。在代理的绘制方法中,可以使用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);
...
希望这有帮助,谢谢
更新0:更改HtmlLegate以使图标可见,并使选定项目的笔颜色不同
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();
}
我的答案主要是从@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());
}
下面是对上述答案组合的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())
这个在PySide。我将QPaint传递给QLabel并让它自己绘制,而不是进行大量自定义绘制。突出显示从其他答案中借用的代码
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)
jbmohler对PyQt5的回答有一个小小的更新:一些类显然已经转移到
qtwidts
这远远超出了我的工资等级(即PyQt5背后的螺母和螺栓知识)
我赞同塞西尔·库里对这个问题的评论中表达的观点。现在是2021年,我们似乎仍然要与这种黑客作斗争。荒唐的迄今为止,与JavaFX相比,Qt5给我留下了深刻的印象。这一缺陷令人失望
class HTMLDelegate( QtWidgets.QStyledItemDelegate ):
def __init__( self ):
super().__init__()
# probably better not to create new QTextDocuments every ms
self.doc = QtGui.QTextDocument()
def paint(self, painter, option, index):
options = QtWidgets.QStyleOptionViewItem(option)
self.initStyleOption(options, index)
painter.save()
self.doc.setTextWidth(options.rect.width())
self.doc.setHtml(options.text)
self.doc.setDefaultFont(options.font)
options.text = ''
options.widget.style().drawControl(QtWidgets.QStyle.CE_ItemViewItem, options, painter)
painter.translate(options.rect.left(), options.rect.top())
clip = QtCore.QRectF(0, 0, options.rect.width(), options.rect.height())
painter.setClipRect(clip)
ctx = QtGui.QAbstractTextDocumentLayout.PaintContext()
ctx.clip = clip
self.doc.documentLayout().draw(painter, ctx)
painter.restore()
def sizeHint( self, option, index ):
options = QtWidgets.QStyleOptionViewItem(option)
self.initStyleOption(option, index)
self.doc.setHtml(option.text)
self.doc.setTextWidth(option.rect.width())
return QtCore.QSize(self.doc.idealWidth(), self.doc.size().height())
写下了另一个答案,说明如何在C++中实现这一点。与目前提供的答案不同的是,这是针对Qt5而不是Qt4。然而,最重要的是,前面的回答忽略了项目代表应该能够对齐指定的文本(例如在
QTreeWidget
中)。此外,我还实现了一种删除富文本的方法,以获得与纯文本委托(在ItemView中)一致的感觉
因此,不用多说,下面是我的RichTextDelegate
代码:
void RichTextItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &inOption,
const QModelIndex &index) const {
QStyleOptionViewItem option = inOption;
initStyleOption(&option, index);
if (option.text.isEmpty()) {
// This is nothing this function is supposed to handle
QStyledItemDelegate::paint(painter, inOption, index);
return;
}
QStyle *style = option.widget ? option.widget->style() : QApplication::style();
QTextOption textOption;
textOption.setWrapMode(option.features & QStyleOptionViewItem::WrapText ? QTextOption::WordWrap
: QTextOption::ManualWrap);
textOption.setTextDirection(option.direction);
QTextDocument doc;
doc.setDefaultTextOption(textOption);
doc.setHtml(option.text);
doc.setDefaultFont(option.font);
doc.setDocumentMargin(0);
doc.setTextWidth(option.rect.width());
doc.adjustSize();
if (doc.size().width() > option.rect.width()) {
// Elide text
QTextCursor cursor(&doc);
cursor.movePosition(QTextCursor::End);
const QString elidedPostfix = "...";
QFontMetrics metric(option.font);
#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
int postfixWidth = metric.horizontalAdvance(elidedPostfix);
#else
int postfixWidth = metric.width(elidedPostfix);
#endif
while (doc.size().width() > option.rect.width() - postfixWidth) {
cursor.deletePreviousChar();
doc.adjustSize();
}
cursor.insertText(elidedPostfix);
}
// Painting item without text (this takes care of painting e.g. the highlighted for selected
// or hovered over items in an ItemView)
option.text = QString();
style->drawControl(QStyle::CE_ItemViewItem, &option, painter, inOption.widget);
// Figure out where to render the text in order to follow the requested alignment
QRect textRect = style->subElementRect(QStyle::SE_ItemViewItemText, &option);
QSize documentSize(doc.size().width(), doc.size().height()); // Convert QSizeF to QSize
QRect layoutRect = QStyle::alignedRect(Qt::LayoutDirectionAuto, option.displayAlignment, documentSize, textRect);
painter->save();
// Translate the painter to the origin of the layout rectangle in order for the text to be
// rendered at the correct position
painter->translate(layoutRect.topLeft());
doc.drawContents(painter, textRect.translated(-textRect.topLeft()));
painter->restore();
}
QSize RichTextItemDelegate::sizeHint(const QStyleOptionViewItem &inOption, const QModelIndex &index) const {
QStyleOptionViewItem option = inOption;
initStyleOption(&option, index);
if (option.text.isEmpty()) {
// This is nothing this function is supposed to handle
return QStyledItemDelegate::sizeHint(inOption, index);
}
QTextDocument doc;
doc.setHtml(option.text);
doc.setTextWidth(option.rect.width());
doc.setDefaultFont(option.font);
doc.setDocumentMargin(0);
return QSize(doc.idealWidth(), doc.size().height());
}
谢谢你的回复。实际上,我在玩重写委托和QTextDocument。但是,项目的大小存在问题。您的答案指向
initStyleOption
和widget->style()->drawControl
。除了两个问题外,您的解决方案非常出色。1.文本正在项目图标2上绘制。所选项目应具有另一种文本颜色。试图找出解决方法。请检查原始帖子的更新;更改发生在HtmlLegate::paint方法中。为了使图标可见,我只是将文本向右移动到图标的宽度。至于文本颜色,我必须更改paint上下文对象的文本颜色的调色板设置。希望这就是你要找的,regards@Anton您知道如何修改选定的文本颜色了吗?适用于tableWidgets,你是个英雄。我如何指示QTextDocument将AppApplications样式表作为默认值继承?请注意,如果optionV4.state的帐户处于非活动状态,则ctx.palette.setcolor部分需要额外嵌套。否则,当您移动到另一个窗口时,文本几乎无法读取。否则效果很好。ThanksText颜色注释:使用else ctx.palete.setColor(qpalete::Text,optionV4.palete.color(qpalete::Active,qpalete::Text))代码>以确保文本颜色设置正确。通过stylesheet.QTextDocument设置使用非默认文本颜色时需要:如果添加doc.setDocumentMargin(0);doc.setDefaultFont(optionV4.font)代码>(将其添加到paint和sizeHint中),然后通过样式表更改字体时,字体将是正确的。此外,sizeHint例程中的doc.setTextWidth
调用似乎没有任何作用。如果同时使用sizeHint
和paint
方法,则当项目的列缩小时,您可以让单词消失,而不是被截断。@Timo在jbmohler答案下面的注释适用于此处,对于QListView
中的长文本非常重要:我将其复制到此处。在第行:doc.setHtml(optionV4.text)之后,还需要设置doc.setTextWidth(optionV4.rect.width()),否则代理无法正确呈现目标绘图区域的较长内容。例如,不在QListView中包装单词。此版本似乎无法处理为项目视图指定的对齐方式。相反,这将始终与左上角对齐。如果对齐对你来说很重要,你可以看看我的答案:真是个黑客!啊,谢谢。高亮显示:如果选择了options.state和QtGui.QStyle.state:doc.setHtml(options.Text)
,则还需要设置doc.setTextWidth(option.rect.width())
,否则,代理将无法针对目标绘图区域正确渲染较长的内容。例如,在QListView中不包装单词。对我来说不起作用,我只看到一小部分随机文本和一部分条目。对于那些需要它的人:我修改了@Pepijn answer一点,以便在Qt API中也包含多行标签,这很可笑。2019年,这应该是内置功能。当每个想要格式化项文本的Qt应用程序(…我们承认,它们中的大多数)需要手动重新实现没有人成功实现的非平凡项委托时
void RichTextItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &inOption,
const QModelIndex &index) const {
QStyleOptionViewItem option = inOption;
initStyleOption(&option, index);
if (option.text.isEmpty()) {
// This is nothing this function is supposed to handle
QStyledItemDelegate::paint(painter, inOption, index);
return;
}
QStyle *style = option.widget ? option.widget->style() : QApplication::style();
QTextOption textOption;
textOption.setWrapMode(option.features & QStyleOptionViewItem::WrapText ? QTextOption::WordWrap
: QTextOption::ManualWrap);
textOption.setTextDirection(option.direction);
QTextDocument doc;
doc.setDefaultTextOption(textOption);
doc.setHtml(option.text);
doc.setDefaultFont(option.font);
doc.setDocumentMargin(0);
doc.setTextWidth(option.rect.width());
doc.adjustSize();
if (doc.size().width() > option.rect.width()) {
// Elide text
QTextCursor cursor(&doc);
cursor.movePosition(QTextCursor::End);
const QString elidedPostfix = "...";
QFontMetrics metric(option.font);
#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
int postfixWidth = metric.horizontalAdvance(elidedPostfix);
#else
int postfixWidth = metric.width(elidedPostfix);
#endif
while (doc.size().width() > option.rect.width() - postfixWidth) {
cursor.deletePreviousChar();
doc.adjustSize();
}
cursor.insertText(elidedPostfix);
}
// Painting item without text (this takes care of painting e.g. the highlighted for selected
// or hovered over items in an ItemView)
option.text = QString();
style->drawControl(QStyle::CE_ItemViewItem, &option, painter, inOption.widget);
// Figure out where to render the text in order to follow the requested alignment
QRect textRect = style->subElementRect(QStyle::SE_ItemViewItemText, &option);
QSize documentSize(doc.size().width(), doc.size().height()); // Convert QSizeF to QSize
QRect layoutRect = QStyle::alignedRect(Qt::LayoutDirectionAuto, option.displayAlignment, documentSize, textRect);
painter->save();
// Translate the painter to the origin of the layout rectangle in order for the text to be
// rendered at the correct position
painter->translate(layoutRect.topLeft());
doc.drawContents(painter, textRect.translated(-textRect.topLeft()));
painter->restore();
}
QSize RichTextItemDelegate::sizeHint(const QStyleOptionViewItem &inOption, const QModelIndex &index) const {
QStyleOptionViewItem option = inOption;
initStyleOption(&option, index);
if (option.text.isEmpty()) {
// This is nothing this function is supposed to handle
return QStyledItemDelegate::sizeHint(inOption, index);
}
QTextDocument doc;
doc.setHtml(option.text);
doc.setTextWidth(option.rect.width());
doc.setDefaultFont(option.font);
doc.setDocumentMargin(0);
return QSize(doc.idealWidth(), doc.size().height());
}