Ho risolto il problema mettendo insieme poche risposte e guardando gli interni di Qt.
Una soluzione che funziona molto veloce per il contenuto HTML statico con collegamenti in QTableView
è folows:
- sottoclasse
QTableView
e gestire gli eventi del mouse là;
- Sottoclasse
QStyledItemDelegate
e dipingi il codice HTML lì (contrariamente alla risposta di RazrFalcon, è molto veloce, poiché solo una piccola quantità di celle è visibile alla volta e solo quelle hanno il metodo paint()
chiamato);
- Nella sottoclasse
QStyledItemDelegate
creare una funzione che indovina quale collegamento è stato selezionato da QAbstractTextDocumentLayout::anchorAt()
. Non è possibile creare da soli QAbstractTextDocumentLayout
, ma è possibile ottenerlo da QTextDocument::documentLayout()
e, in base al codice sorgente Qt, è garantito che non sia nullo.
- In sottoclasse
QTableView
modificare QCursor
forma del puntatore di conseguenza per se è in bilico su un collegamento
Di seguito è riportato un completo, implementazione di lavoro di QTableView
e QStyledItemDelegate
sottoclassi che dipingono il codice HTML e inviano segnali su Link hover/attivazione. Il delegato e il modello devono ancora essere impostato dall'esterno, come segue:
wordTable->setModel(&myModel);
auto wordItemDelegate = new WordItemDelegate(this);
wordTable->setItemDelegate(wordItemDelegate); // or just choose specific columns/rows
WordView.h
class WordView : public QTableView {
Q_OBJECT
public:
explicit WordView(QWidget *parent = 0);
signals:
void linkActivated(QString link);
void linkHovered(QString link);
void linkUnhovered();
protected:
void mousePressEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
private:
QString anchorAt(const QPoint &pos) const;
private:
QString _mousePressAnchor;
QString _lastHoveredAnchor;
};
WordView.cpp
#include <QApplication>
#include <QCursor>
#include <QMouseEvent>
#include "WordItemDelegate.h"
#include "WordView.h"
WordView::WordView(QWidget *parent) :
QTableView(parent)
{
// needed for the hover functionality
setMouseTracking(true);
}
void WordView::mousePressEvent(QMouseEvent *event) {
QTableView::mousePressEvent(event);
auto anchor = anchorAt(event->pos());
_mousePressAnchor = anchor;
}
void WordView::mouseMoveEvent(QMouseEvent *event) {
auto anchor = anchorAt(event->pos());
if (_mousePressAnchor != anchor) {
_mousePressAnchor.clear();
}
if (_lastHoveredAnchor != anchor) {
_lastHoveredAnchor = anchor;
if (!_lastHoveredAnchor.isEmpty()) {
QApplication::setOverrideCursor(QCursor(Qt::PointingHandCursor));
emit linkHovered(_lastHoveredAnchor);
} else {
QApplication::restoreOverrideCursor();
emit linkUnhovered();
}
}
}
void WordView::mouseReleaseEvent(QMouseEvent *event) {
if (!_mousePressAnchor.isEmpty()) {
auto anchor = anchorAt(event->pos());
if (anchor == _mousePressAnchor) {
emit linkActivated(_mousePressAnchor);
}
_mousePressAnchor.clear();
}
QTableView::mouseReleaseEvent(event);
}
QString WordView::anchorAt(const QPoint &pos) const {
auto index = indexAt(pos);
if (index.isValid()) {
auto delegate = itemDelegate(index);
auto wordDelegate = qobject_cast<WordItemDelegate *>(delegate);
if (wordDelegate != 0) {
auto itemRect = visualRect(index);
auto relativeClickPosition = pos - itemRect.topLeft();
auto html = model()->data(index, Qt::DisplayRole).toString();
return wordDelegate->anchorAt(html, relativeClickPosition);
}
}
return QString();
}
WordItemDelegate.h
#include <QStyledItemDelegate>
class WordItemDelegate : public QStyledItemDelegate {
Q_OBJECT
public:
explicit WordItemDelegate(QObject *parent = 0);
QString anchorAt(QString html, const QPoint &point) const;
protected:
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
};
WordItemDelegate.cpp
#include <QPainter>
#include <QTextDocument>
#include <QAbstractTextDocumentLayout>
#include "WordItemDelegate.h"
WordItemDelegate::WordItemDelegate(QObject *parent) :
QStyledItemDelegate(parent)
{}
QString WordItemDelegate::anchorAt(QString html, const QPoint &point) const {
QTextDocument doc;
doc.setHtml(html);
auto textLayout = doc.documentLayout();
Q_ASSERT(textLayout != 0);
return textLayout->anchorAt(point);
}
void WordItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const {
auto options = option;
initStyleOption(&options, index);
painter->save();
QTextDocument doc;
doc.setHtml(options.text);
options.text = "";
options.widget->style()->drawControl(QStyle::CE_ItemViewItem, &option, 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 WordItemDelegate::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());
}
noti che questa soluzione è veloce solo perché un piccolo sottoinsieme di righe è reso in una volta, e quindi non molti QTextDocument
s sono resi contemporaneamente. La regolazione automatica di tutte le altezze delle righe o delle colonne contemporaneamente sarà comunque lenta. Se hai bisogno di questa funzionalità, puoi fare in modo che il delegato informi la visualizzazione che ha dipinto qualcosa e quindi, se non lo ha fatto, regola la larghezza e l'altezza della vista. Combinalo con QAbstractItemView::rowsAboutToBeRemoved
per rimuovere le informazioni memorizzate nella cache e hai una soluzione funzionante. Se sei pignolo riguardo alla dimensione e alla posizione della barra di scorrimento, puoi calcolare l'altezza media in base a pochi elementi campione in QAbstractItemView::rowsInserted
e ridimensionare il resto di conseguenza senza sizeHint
.
Riferimenti: risposta
Risposta perfetta per quello di cui avevo bisogno. Grazie. –