Доброго времени суток!..
Задача - отображать в таблице и/или списке форматированный текст (переносы строки, различные шрифты и пр. смотри, как вариант, firefox). Делать виджета-наследника не очень желательно. Хочется использовать делегаты. Подскажите, с какого боку лучше подойти? Отрисовывать итемы в виде различных контролов я научился (в прошлой теме Delegate ). Предполагал это и использовать, но что-то застопорился, не найдя подходящего контрола QStyle::CE_ , умеющего отображать, скажем, html.
jim1406, с тебя пиво
Шучу.
Интересную ты задачку задал, если честно не сразу справился. Но было очень интерестно повозиться, т.ч. спасибо.
В архиве готовый делегат, с тестовым примером. Обрати внимание на параметр Qt::Alignment align, который передается в конструкторе, он позволяет ориентировать текст в ячейке.
#include <QtGui>
#include "htmltextdelegate.h"
void HtmlDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
QString str = index.data(Qt::DisplayRole).toString();
QTextDocument td;
td.setHtml(str);
QStyleOptionViewItem opt = option;
QRectF lr = layoutRect(td, opt.rect);
painter->save();
painter->translate(lr.topLeft());
painter->setClipRect(lr.translated(-lr.x(), -lr.y()));
td.drawContents(painter, QRectF());
painter->restore();
}
QWidget *HtmlDelegate::createEditor(QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
QTextEdit *textEdit = new QTextEdit(parent);
return textEdit;
}
void HtmlDelegate::setEditorData(QWidget *editor,
const QModelIndex &index) const
{
QString str = index.data(Qt::DisplayRole).toString();
QTextEdit *textEdit = qobject_cast<QTextEdit*>(editor);
textEdit->setHtml(str);
}
void HtmlDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) const
{
QTextEdit *textEdit = qobject_cast<QTextEdit*>(editor);
QString str = textEdit->toHtml();
model->setData(index, str, Qt::DisplayRole);
}
QRectF HtmlDelegate::layoutRect(const QTextDocument & td, QRect rect) const
{
QRectF rectf;
qreal xo;
qreal yo;
if(align & Qt::AlignLeft) {
xo = 0;
} else if(align & Qt::AlignRight) {
qreal rw = td.documentLayout()->documentSize().width();
xo = 0;
xo = qMax((rect.width()-rw), qreal(0));
} else if(align & Qt::AlignHCenter) {
qreal rw = td.documentLayout()->documentSize().width();
xo = 0;
xo = qMax((rect.width()-rw)/2, qreal(0));
}
if(align & Qt::AlignTop) {
yo = 0;
} else if(align & Qt::AlignBottom) {
qreal rh = td.documentLayout()->documentSize().height();
yo = 0;
yo = qMax((rect.height()-rh), qreal(0));
} else if(align & Qt::AlignVCenter) {
qreal rh = td.documentLayout()->documentSize().height();
yo = 0;
yo = qMax((rect.height()-rh)/2, qreal(0));
}
return QRectF(xo + rect.x(), yo + rect.y(), rect.width(), rect.height());
}
if(align & Qt::AlignLeft) {
xo = 0;
}
Не могу сообразить, как в QComboBox нормально такие итемы отрисовать . Там такого ресайза, как в таблице нету...
Изначально идея была в выпадающем списке отрисовывать сложно-форматированный текст...
Хочу напомнить о существовании одной статьи в 24 выпуске Qt Quarterly: http://doc.trolltech.com/qq/qq24-textlayouts.html
(она есть и на этом сайте, но тут какой-то косяк с форматированием кода - http://www.crossplatform.ru/?q=node/544
Имхо QTextLayout полегковеснее будет, чем весь QTextDocument. Это конечно, если людям понадобится только отображение форматированного текста.
Или вообще использовать QTextLine, если надо отрисовать всего-лишь одну строку:
void QTextLine::draw ( QPainter * painter, const QPointF & position, const QTextLayout::FormatRange * selection = 0 ) const
Долго ли коротко ли получилось следующее:
void HtmlDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
QTextDocument td;
td.setHtml(index.data(Qt::DisplayRole).toString());
td.adjustSize();
QAbstractTextDocumentLayout::PaintContext context;
context.palette = option.palette;
painter->save();
QAbstractTextDocumentLayout *layout = td.documentLayout();
painter->translate(0, option.rect.y());
layout->draw(painter, context);
painter->restore();
}
QSize HtmlDelegate::sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index) const
{
QTextDocument td;
td.setHtml(index.data(Qt::DisplayRole).toString());
td.adjustSize();
return td.documentLayout()->documentSize().toSize();
}
Q_D(SomeClass)
d->someFunc();
SomeClassPrivate *const d = d_func(); // получает указатель на внутренние данные
d->someFunc();
следовательно смотри соответствующий метод этого внутреннего класса (обычно этот класс описан раньше основного)
Q_D(SomeClass)
d->someFunc();
SomeClassPrivate *const d = d_func(); // получает указатель на внутренние данные
d->someFunc();
следовательно смотри соответствующий метод этого внутреннего класса (обычно этот класс описан раньше основного)Q_D(SomeClass)
d->someFunc();
SomeClassPrivate *const d = d_func(); // получает указатель на внутренние данные
d->someFunc();
следовательно смотри соответствующий метод этого внутреннего класса (обычно этот класс описан раньше основного)#include <QtGui>
#include "htmltextdelegate.h"
void HtmlDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
QString str = index.data(Qt::DisplayRole).toString();
QTextDocument td;
td.setHtml(str);
QStyleOptionViewItem opt = option;
QRectF lr = layoutRect(td, opt.rect);
painter->save();
painter->translate(lr.topLeft());
painter->setClipRect(lr.translated(-lr.x(), -lr.y()));
td.drawContents(painter, QRectF());
painter->restore();
}
QWidget *HtmlDelegate::createEditor(QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
QTextEdit *textEdit = new QTextEdit(parent);
return textEdit;
}
void HtmlDelegate::setEditorData(QWidget *editor,
const QModelIndex &index) const
{
QString str = index.data(Qt::DisplayRole).toString();
QTextEdit *textEdit = qobject_cast<QTextEdit*>(editor);
textEdit->setHtml(str);
}
void HtmlDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) const
{
QTextEdit *textEdit = qobject_cast<QTextEdit*>(editor);
QString str = textEdit->toHtml();
model->setData(index, str, Qt::DisplayRole);
}
QRectF HtmlDelegate::layoutRect(const QTextDocument & td, QRect rect) const
{
QRectF rectf;
qreal xo;
qreal yo;
if(align & Qt::AlignLeft) {
xo = 0;
} else if(align & Qt::AlignRight) {
qreal rw = td.documentLayout()->documentSize().width();
xo = 0;
xo = qMax((rect.width()-rw), qreal(0));
} else if(align & Qt::AlignHCenter) {
qreal rw = td.documentLayout()->documentSize().width();
xo = 0;
xo = qMax((rect.width()-rw)/2, qreal(0));
}
if(align & Qt::AlignTop) {
yo = 0;
} else if(align & Qt::AlignBottom) {
qreal rh = td.documentLayout()->documentSize().height();
yo = 0;
yo = qMax((rect.height()-rh), qreal(0));
} else if(align & Qt::AlignVCenter) {
qreal rh = td.documentLayout()->documentSize().height();
yo = 0;
yo = qMax((rect.height()-rh)/2, qreal(0));
}
return QRectF(xo + rect.x(), yo + rect.y(), rect.width(), rect.height());
}
void Header::paintEvent(QPaintEvent *e)
{
if (!count()) return;
QPainter painter(viewport());
QRect currentSectionRect;
const int height = viewport()->height();
for (int i = 0; i != model()->columnCount(); ++i) {
painter.save();
currentSectionRect.setRect(sectionViewportPosition(i), 0, sectionSize(i), height);
QStyleOptionHeader opt;
initStyleOption(&opt);
opt.rect = currentSectionRect;
opt.section = i;
style()->drawControl(QStyle::CE_HeaderSection, &opt, &painter, this);
QString str = model()->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString();
QTextDocument td;
td.setHtml(str);
QRectF lr = layoutRect(td, currentSectionRect);
painter.translate(lr.topLeft());
painter.setClipRect(lr.translated(-lr.x(), -lr.y()));
td.drawContents(&painter, QRectF());
painter.restore();
}
}
Спасибо! Только сделаю это с помощью eventFilter, дабы лишний раз не наследовать.
Кстати, с твоим подходом было бы логичнее использовать QHeaderView::paintSection.
Что я делаю не так? В конструкторе делаю
m_table_view->horizontalHeader()->installEventFilter(this);
bool SoilsWidget::eventFilter(QObject *watched, QEvent *event)
{
if ( event->type() == QEvent::Paint ) {
qDebug() << watched << event;
return true;
} else {
return QWidget::eventFilter(watched, event);
}
}
фильтр нужно устанавливать для viewport заголовка
m_table_view->horizontalHeader()->viewport()->installEventFilter(this);
спасибо! правда, решение с фильтром не прокатило, ибо используются защищённые методы, пришлось унаследовать.
ты там метод layoutRect используешь... что это? компилятор ругается на его отсутствие, в документации не нашёл...
Да уж, при насследовании, оказывается, не удастся восстановить все фичи QHeaderView. Например, учитывать позицию мыши при отрисовке (в некоторых стилях она учитывается). Не получится, так как в оригинальном методе прорисовки используются некоторые приватные методы.
Пока решил повременить с рендеринг rich text у себя в программе. Заодно проголосовал за баг Qt: http://bugreports.qt.nokia.com/browse/QTBUG-2380.
Кстати, насчёт изначального вопроса темы. Вот более короткое (и менее полное) решение: http://developer.qt.nokia.com/faq/answer/how_can_i_have_richtext_in_my_qtableview
Форум Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)