Suppose my model has items with the following string for Qt::DisplayRole
<span>blah-blah <b>some text</b> other blah</span>
I want QTreeView (actually, any item view) to render it like a rich text. Instead, item views render it like a pure text by default. How to achieve the desired rendering?
Actually, this is a search results model. User enters a text, some document is searched against that text and the user is presented with search results, where the words being searched should be bolder than surrounding text.
I guess you can use setItemDelegate method of the treeview to setup custom painter for your treeview items. In the delegate's paint method you can use QTextDocument to load item's text as html and render it. Please check if an example below would work for you:
treeview initialization:
...
// 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);
...
custom delegate implementation
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());
}
hope this helps, regards
update0: changes to HTMLDelegate to make icons visible and different pen color for selected items
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();
}
My answer is mostly inspired by #serge_gubenko's one. However, there were made several improvements so that the code is finally useful in my 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());
}
Here's the PyQt conversion of the combination of the above answers that worked for me. I would expect this to work virtually identically for PySide as well.
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())
This one is in PySide. Rather than doing a lot of custom drawing, I pass the QPainter to the QLabel and make it draw itself. Highlighting code borrowed from other answers.
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)
Writing up yet another answer for how this can be done in C++. The difference to the answers provided so far is that this is for Qt5 and not Qt4. Most importantly however the previous answers neglected that the item delegate should be able to align the text as specified (e.g. in a QTreeWidget). Additionally I also implemented a way to elide rich text in order to get a consistent feeling with plaintext delegates (in ItemViews).
So without further ado, here is my code for a 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());
}
Just a slight update from jbmohler's answer, for PyQt5: some classes have apparently been shifted to QtWidgets.
This is way beyond my paygrade (i.e. knowledge of the nuts and bolts behind PyQt5).
I echo the sentiment expressed in Cecil Curry's comment to the question. It is now 2021, and we appear still to have to struggle with this sort of hack. Ridiculous. I've been impressed by Qt5 to date, as compared to JavaFX for example. This deficiency is a let-down.
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())
Related
I have custom model that return necessary foreground color for the ForegroundRole:
QVariant AlertTreeModel::data(const QModelIndex& index, int role) const
{
if (!index.isValid())
{
return {};
}
const auto item = itemForIndex(index);
switch (role)
{
case Qt::ForegroundRole:
return item->color(); // return different colors for the different item states
default:
return {};
}
}
View (bit simplified code):
void UniformIndentationTreeView::drawRow(QPainter* painter, const QStyleOptionViewItem& options, const QModelIndex& index) const
{
QStyleOptionViewItem opt = options;
QTreeView::drawRow(painter, opt, index);
}
I also have the simple custom delegate where I can ensure I get correct foreground color:
void AlertTreeViewItemDelegate::initStyleOption(QStyleOptionViewItem* option, const QModelIndex& index) const
{
UniformIndentationItemViewDelegate::initStyleOption(option, index);
QColor color = index.data(Qt::ForegroundRole).value<QColor>();
INFOLOG << "tree item color = " << color.name(QColor::HexRgb);
option->palette.setColor(QPalette::WindowText, color);
option->palette.setColor(QPalette::HighlightedText, color);
}
The problem is that my tree is painted with colors from a style sheet and doesn't use the colors which the model returns. How to force view/delegate to use custom foreground color?
Update. In the Qt sources we can see that QStyledItemDelegate::paint take widget's style to draw controls.
void QStyledItemDelegate::paint(QPainter *painter,
const QStyleOptionViewItem &option, const QModelIndex &index) const
{
Q_ASSERT(index.isValid());
QStyleOptionViewItem opt = option;
initStyleOption(&opt, index);
const QWidget *widget = QStyledItemDelegatePrivate::widget(option);
QStyle *style = widget ? widget->style() : QApplication::style();
style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, widget);
}
How to draw items with colors from a model, not from a style sheet?
I have a delegate MyDelegate which is used for QListWidget. The delegate is derived from QStyledItemDelegate. One of the goals of MyDelegate is to place a checkbox button on each row of ListWidget. It is done within the paint() event of MyDelegate:
void MyDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QStyledItemDelegate::paint(painter, option, index);
// ... drawing other delegate elements
QStyleOptionButton checkbox;
// setting up checkbox's size and position
// now draw the checkbox
QApplication::style()->drawControl(QStyle::CE_CheckBox, &checkbox, painter);
}
At first I thought the checkbox would automatically change its state on click, since I specified QStyle::CE_CheckBox. But it is not the case. Looks like I have to specify the checkbox visual behavior manually.
Data-wise, When user clicks on that checkbox, certain signal is emitted and the scene data is changed. I perform this action in editorEvent():
bool MyDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index)
{
if (event->type() == QEvent::MouseButtonRelease) {
if (/* within checkbox area */)
emit this->clickedCheckbox(index);
}
}
The backend part works. However, I cannot figure out how to make the checkbox button to change its visual state from checked to unchecked, and backwards.
I realized that I can change the checkbox state manually by doing something like this from paint():
checkbox.state = someCondition? (QStyle::State_Enabled | QStyle::State_On) :
(QStyle::State_Enabled | QStyle::State_Off) ;
QStyle::State_On/Off does the trick of manual checkbox state change.
But I do not know how to set up that someCondition and where I should set it up. I tried to introduce it as a private bool variable which would be set in editorEvent() when the checkbox area gets a click, however, it does not produce the desired behavior: it sets all the other checkboxes of ListWidget to the same visual state. So, it behaved like some global condition for all the checkboxes.
I feel like, to accomplish my task, I have to re-implement the button and make it to change the checkbox state on click. But I'm lost on this way and not sure how to approach the problem. From the QStyleOptionButton API I do not see a clicked() or any other method I could use.
So, the question is: how do I make checkbox to behave like a checkbox, visually? If I need to re-implement a checkbox, then what class do I inherit?
You can set some value that describes your checkbox state in MyDelegate::editorEvent and then use it to paint a proper checkbox:
const int CHECK_ROLE = Qt::UserRole + 1;
bool MyDelegate::editorEvent(QEvent *event,
QAbstractItemModel *model,
const QStyleOptionViewItem &option,
const QModelIndex &index)
{
if (event->type() == QEvent::MouseButtonRelease)
{
bool value = index.data(CHECK_ROLE).toBool();
// invert checkbox state
model->setData(index, !value, CHECK_ROLE);
return true;
}
return QStyledItemDelegate::editorEvent(event, model, option, index);
}
void MyDelegate::paint(QPainter *painter,
const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
QStyleOptionButton cbOpt;
cbOpt.rect = option.rect;
bool isChecked = index.data(CHECK_ROLE).toBool();
if (isChecked)
{
cbOpt.state |= QStyle::State_On;
}
else
{
cbOpt.state |= QStyle::State_Off;
}
QApplication::style()->drawControl(QStyle::CE_CheckBox, &cbOpt, painter);
}
I added the following:
data(Qt:CheckStateRole) is toggled.
enable/disable the item.
center the box in the cell.
only toggle checkmark with left mouse button, and when checkmark is clicked (not the entire cell).
Here's the code:
// -------------------------------- //
class GFQtCheckboxItemDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
const int CHECK_ROLE = Qt::CheckStateRole;
// dont't let the default QStyledItemDelegate create the true/false combobox
QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE
{
(void)parent;
(void)option;
(void)index;
return nullptr;
}
QRect GetCheckboxRect(const QStyleOptionViewItem &option)const
{
QStyleOptionButton opt_button;
opt_button.QStyleOption::operator=(option);
QRect sz = QApplication::style()->subElementRect(QStyle::SE_ViewItemCheckIndicator, &opt_button);
QRect r = option.rect;
// center 'sz' within 'r'
int dx = (r.width() - sz.width())/2;
int dy = (r.height()- sz.height())/2;
r.setTopLeft(r.topLeft() + QPoint(dx,dy));
r.setWidth(sz.width());
r.setHeight(sz.height());
return r;
}
// click event
bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index)
{
if (event->type() == QEvent::MouseButtonRelease)
{
QMouseEvent* pME = static_cast<QMouseEvent*>(event);
if(pME->button() == Qt::LeftButton)
{
QRect ro = GetCheckboxRect(option);
QPoint pte = pME->pos();
if(ro.contains(pte) )
{
bool value = index.data( CHECK_ROLE).toBool();
// invert checkbox state
model->setData(index, !value, CHECK_ROLE);
return true;
}
}
}
return QStyledItemDelegate::editorEvent(event, model, option, index);
}
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QStyleOptionButton cbOpt;
cbOpt.rect = GetCheckboxRect(option);
bool isChecked = index.data(CHECK_ROLE ).toBool();
if (isChecked)
{
cbOpt.state |= QStyle::State_On;
}
else
{
cbOpt.state |= QStyle::State_Off;
}
QVariant enabled = index.data(Qt::ItemIsEnabled);
if(enabled.isNull() || enabled.toBool() )
{
cbOpt.state |= QStyle::State_Enabled;
}
QApplication::style()->drawControl(QStyle::CE_CheckBox, &cbOpt, painter);
}
};
And for completeness, here's how to apply it:
m_p_QTableView->setItemDelegateForColumn(iTableColumnIndex, new GFQtCheckboxItemDelegate() );
...
bool yesno ...;
pGrid->setData(index, yesno, Qt::CheckStateRole);
qtqcheckboxqtableview
This is much easier if you change the QListWidget to a QListModel and a QListView.
Qt::ItemFlags ListModel::flags(const QModelIndex &index) const
{
return QAbstractListModel::flags(index) | Qt::ItemIsUserCheckable;
}
QVariant ListModel::data(const QModelIndex &index, int role) const
{
if (role == Qt::EditRole) {
return m_checkStateForRow[index.row()];
}
}
bool ListModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (role == Qt::CheckStateRole) {
m_checkStateForRow[index.row()] = value.toInt();
emit dataChanged(index, index, {role});
return true;
}
return false;
}
I have a QTableView that works very well, the first column holds some thumbnails, in each cell of this column the thumbnails are vertically centered, but not horizontally centered.
Do I really need to use a delegate?
If yes, How to center them horizontally using QStyledItemDelegate?
Construct your own delegate and inherit QStyledItemDelegate. Override the paint method.
Then do something like this:
void
MyDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option,
const QModelIndex& index) const
{
QPixmap pixmap;
pixmap.load("Your pixmap file path");
pixmap = pixmap.scaled(option.rect.width(), option.rect.height(), Qt::KeepAspectRatio);
// Position our pixmap
const int x = option.rect.center().x() - pixmap.rect().width() / 2;
const int y = option.rect.center().y() - pixmap.rect().height() / 2;
if (option.state & QStyle::State_Selected) {
painter->fillRect(option.rect, option.palette.highlight());
}
painter->drawPixmap(QRect(x, y, pixmap.rect().width(), pixmap.rect().height()), pixmap);
}
Drawing by yourself is not necessary, but a custom delegate - is. The styled item delegate uses the style's control element drawing code to draw a CE_ItemViewItem - see the source code for Qt 5.5.0. The drawing code takes the style option's decorationAlignment member into account. Unfortunately, there's no data role that would pass that alignment to the styles's implementation. Instead, you have to override the alignment in your delegate:
class DecorationAligningDelegate : public QStyledItemDelegate {
Q_OBJECT
Qt::Alignment const m_alignment;
public:
explicit DecorationAligningDelegate(Qt::Alignment alignment, QObject * parent = 0) :
QStyledItemDelegate(parent), m_alignment(alignment) {}
Qt::Alignment alignment() const { return m_alignment; }
void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const {
auto opt = option;
opt.decorationAlignment = m_alignment;
QStyledItemDelegate::paint(painter, opt, index);
}
};
Then, to center the thumbnails:
view.setItemDelegateForColumn(0,
new DecorationAligningDelegate(Qt::AlignHCenter, &view));
//or
view->setItemDelegateForColumn(0,
new DecorationAligningDelegate(Qt::AlignHCenter, view));
If you really wished to paint it all yourself, even though it's unnecessary, the rectangle of the item to be painted is given in the style option (option.rect). To draw the pixmap centered in the item's rectangle, you could do as follows:
QStyleOption option;
QPixmap pix;
QPainter painter;
...
painter.save();
auto loc = option.rect.center() - pix.rect().center()
painter.drawPixmap(loc, pix);
painter.restore();
I will just leave my version that literary is a combination of the two answers.
class DecorationAligningDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
explicit DecorationAligningDelegate(Qt::Alignment alignment, QObject *parent = nullptr)
: QStyledItemDelegate(parent), m_alignment(alignment) {}
Qt::Alignment alignment() const { return m_alignment; }
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QIcon icon = QIcon(qvariant_cast<QIcon>(index.data(Qt::DecorationRole)));
if (option.state & QStyle::State_Selected)
{
painter->fillRect(option.rect, option.palette.highlight());
}
icon.paint(painter, option.rect, m_alignment);
}
private:
Q_DISABLE_COPY(VDecorationAligningDelegate)
Qt::Alignment const m_alignment;
};
I assume you define your item like this:
auto *item = new QTableWidgetItem();
item->setIcon(QIcon("Your pixmap file path"));
Don't forget about setting a delegate.
I am working on QTableview inside a QStandardItemModel. I am using QTextEdit inside a class derived from Qdeligates. This allows me to insert newline inside tableview.
Most of the things are working fine but now i am facing a challenge that when ever i click a cell to edit QTextEdit is opening on the left corner of the GUI. When I click cell row 0, column 3 text edit opens on the left corner.
How can i make my QTextEdit to open near the cell which i am editing ?
Also what should i put inside this function updateEditorGeometry ?
Here is my initialising code for tableview :--
ui->testCaseTableView->verticalHeader()->resizeSections(QHeaderView::ResizeToContents); //---> original
ui->testCaseTableView->verticalHeader()->setResizeMode(QHeaderView::ResizeToContents);
ui->testCaseTableView->horizontalHeader()->setStretchLastSection(true);
//Set model & deligate
ui->testCaseTableView->setModel(model);
ui->testCaseTableView->setItemDelegate(mydeligate);
Here is my code for the deligate :---
QWidget* textViewDeligate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QTextEdit *tableEdit = new QTextEdit(parent);
return tableEdit;
}
void textViewDeligate::setEditorData ( QWidget * editor, const QModelIndex & index ) const
{
QString value = index.model()->data(index,Qt::EditRole).toString();
QTextEdit *tableEditCopy = static_cast<QTextEdit*>(editor);
tableEditCopy->setPlainText(value);
}
void textViewDeligate::setModelData ( QWidget * editor, QAbstractItemModel * model, const QModelIndex & index ) const
{
QTextEdit *tableEditCopy = static_cast<QTextEdit*>(editor);
QString str = tableEditCopy->toPlainText();
model->setData(index, str, Qt::EditRole);
}
void textViewDeligate::updateEditorGeometry ( QWidget *editor, const QStyleOptionViewItem & option, const QModelIndex & index ) const
{
}
You need to setup the editor geometry of all widgets you create yourself. This is so that the widgets get given the correct dimensions of the tables' cells.
void textViewDeligate::updateEditorGeometry ( QWidget *editor, const QStyleOptionViewItem & option, const QModelIndex & index ) const
{
editor->setGeometry(option.rect);
}
This was what improved it further :---
editor->setGeometry(option.rect.x(),option.rect.y(),editor->sizeHint().width(),editor->sizeHint().height());
I want my AbstracttableModel subclass data() method to return html i.e.
PreText<b>Text</b>PostText
And this text must be displayed int table as in html:
PreTextTextPostText
How can I do this?
You can create a delegate for the view that will display the html.
class HtmlDelegate : public QItemDelegate {
public:
HtmlDelegate(QObject *parent = 0) : QItemDelegate(parent) {}
// This function is only called to paint the text
void drawDisplay(QPainter *painter, const QStyleOptionViewItem &option,
const QRect &rect, const QString &text) const
{
QTextDocument doc;
// Since the QTextDocument will do all the rendering, the color,
// and the font have to be put back inside the doc
QPalette::ColorGroup cg = option.state & QStyle::State_Enabled
? QPalette::Normal : QPalette::Disabled;
if (cg == QPalette::Normal && !(option.state & QStyle::State_Active))
cg = QPalette::Inactive;
QColor textColor = option.palette.color(cg, QPalette::Text);
doc.setDefaultStyleSheet(QString("body { color: %1}")
.arg(textColor.name()));
doc.setDefaultFont(option.font);
doc.setHtml(text);
doc.setDocumentMargin(1); // the default is 4 which is too much
painter->save();
painter->translate(rect.topLeft());
doc.drawContents(painter);
painter->restore();
}
// bold and underlined characters take more space
// so you have to redefine this function as well
// (if you have a checkbox or an icon in the item, you will have
// to include their size to the returned value)
QSize sizeHint(const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
QTextDocument doc;
doc.setDefaultFont(option.font);
doc.setHtml(index.data(Qt::DisplayRole).toString());
doc.setDocumentMargin(1);
return doc.size().toSize();
}
};
Then assign it to a view:
view->setItemDelegateForColumn(0, new HtmlDelegate(view));
alexisdm answer works fine, but maybe using a QLabel is lighter than QTextDocument and it works smartly too:
class HtmlDelegateWithLabel : public QItemDelegate
{
public:
HtmlDelegateWithLabel(QObject *parent = 0) : QItemDelegate(parent)
{
}
inline void setupLabel( QLabel& label, const QModelIndex &index ) const
{
QString txt = index.model()->data( index, Qt::DisplayRole ).toString();
label.setText( txt );
}
// This function is only called to paint the text
void drawDisplay(QPainter *painter, const QStyleOptionViewItem &option,
const QRect &rect, const QString &text) const
{
QLabel label;
setupLabel( label, option.index );
label.setEnabled( option.state & QStyle::State_Enabled );
label.setAttribute(Qt::WA_TranslucentBackground);
painter->save();
painter->translate(rect.topLeft());
label.resize( rect.size() );
label.render( painter );
painter->restore();
}
QSize sizeHint(const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
QLabel label;
setupLabel( label, index );
return label.sizeHint();
}
};
Moreover, text gets aligned vertically on the row, which is not the case when using QTextDocument.