What is the best way to present a clickable URL in a QTableView
(or QTreeView
, QListView
, etc...)
Given a QStandardItemModel
where some of the columns contain text with URLs I'd like them to become clickable and then handle the click by using QDesktopServices::openURL()
I was hoping there would be some easy way to leverage QLabel
's textInteraction flags and to cram them into the table. I can't believe there's not an easier way to handle this. I really hope I'm missing something.
You'll need to create a delegate to do the painting. The code should look something like this:
void RenderLinkDelegate::paint(
QPainter *painter,
const QStyleOptionViewItem &option,
const QModelIndex &index
) const
{
QString text = index.data(Qt::DisplayRole).toString();
if (text.isEmpty())
return;
painter->save();
// I only wanted it for mouse over, but you'll probably want to remove
// this condition
if (option.state & QStyle::State_MouseOver)
{
QFont font = option.font;
font.setUnderline(true);
painter->setFont(font);
painter->setPen(option.palette.link().color());
}
painter->drawText(option.rect, Qt::AlignLeft | Qt::AlignVCenter, text);
painter->restore();
}
Well, you can use delegates to render rich text in a qtableview with custom delegates reimplementing the paint method such as:
void CHtmlDelegate::paint(QPainter *painter,
const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
QStyleOptionViewItemV4 opt(option);
QLabel *label = new QLabel;
label->setText(index.data().toString());
label->setTextFormat(Qt::RichText);
label->setGeometry(option.rect);
label->setStyleSheet("QLabel { background-color : transparent; }");
painter->translate(option.rect.topLeft());
label->render(painter);
painter->translate(-option.rect.topLeft());
}
However, it will not make hyperlinks clickable.
To do so, you can use the following hack. Reimplement the setModel method of your table/list view and use setIndexWidget.
void MyView::setModel(QAbstractItemModel *m)
{
if (!m)
return;
QTableView::setModel(m);
const int rows = model()->rowCount();
for (int i = 0; i < rows; ++i)
{
QModelIndex idx = model()->index(i, 1);
QLabel *label = new QLabel;
label->setTextFormat(Qt::RichText);
label->setText(model()->data(idx, CTableModel::HtmlRole).toString());
label->setOpenExternalLinks(true);
setIndexWidget(idx, label);
}
}
In the example above, I replace column 1 with qlabels. Note that you need to void the display role in the model to avoid overlapping data.
Anyway, I would be interested in a better solution based on delegates.
Sadly, its not that easy to render a QLabel
with setOpenExternalLinks()
when using a QTableView
(as opposed to using a QTableWidget
). There are no magic two lines of code you can call and have the job done.
QTextDocument
combined with setHTML()
to render a html linka href
painter->setWidgetToCell()
--
void LabelColumnItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
if( option.state & QStyle::State_Selected )
painter->fillRect( option.rect, option.palette.highlight() );
painter->save();
QTextDocument document; // <---- RichText
document.setTextWidth(option.rect.width());
QVariant value = index.data(Qt::DisplayRole);
if (value.isValid() && !value.isNull())
{
document.setHtml(value.toString()); // <---- make sure model contains html
painter->translate(option.rect.topLeft());
document.drawContents(painter);
}
painter->restore();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With