Как уже обсуждалось, использование делегата является правильным способом сделать это.
В случае, если по какой-то причине это невозможно, я настоятельно рекомендую вам не смешивать вид модели с графической сценой таким образом.
Если использовать делегатов абсолютно невозможно, я бы подумал о рисовании линий на QTableView
- у него есть API для доступа к индексам модели через координаты пикселей, он знает свою позицию прокрутки и имеет обработчики для все необходимые пользовательские данные. Поэтому все, что вам нужно сделать, это сохранить коллекцию QLine
в классе, производном от QTableView
, и нарисовать линии, которые соответствуют текущему окну просмотра, через QPainter
. Боюсь, что даже если «оверлей» с QGraphicsScene
выглядит сейчас как самое простое решение, в сущности, это куча проблем, связанных с обработкой / маршрутизацией пользовательского ввода. И это определенно не Qt-way.
UPD: пример на основе делегата:
Идея довольно проста:
- вся строка представляет собой набор сегментов - каждая ячейка является сегментом;
- для каждой ячейки, сопоставьте линию
start
и end
x-координату с диапазоном [0.0; 1.0]; - сохраняет сопоставленные координаты в самой модели.
Затем вы можете нарисовать свой пользовательский делегат следующим образом:
void LineDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QStyledItemDelegate::paint(painter, option, index);
const QVariant &lineData = index.data(Line::DataRole);
if (lineData.isValid() && lineData.canConvert<Line>()) {
const Line &line = lineData.value<Line>();
const QLineF &lineF = line.toQLine(option.rect);
painter->save();
painter->setPen(m_linePen);
painter->drawLine(lineF);
painter->restore();
}
}
Работа с мышью немного сложнее:
bool LineDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option,
const QModelIndex &index)
{
switch (event->type()) {
case QEvent::MouseButtonPress:
handleMousePress(static_cast<QMouseEvent *>(event), model, option, index);
break;
case QEvent::MouseMove:
handleMouseMove(static_cast<QMouseEvent *>(event), model, option, index);
break;
case QEvent::MouseButtonRelease:
handleMouseRelease(static_cast<QMouseEvent *>(event), model, option, index);
break;
default:
break;
}
return QStyledItemDelegate::editorEvent(event, model, option, index);
}
void LineDelegate::handleMousePress(QMouseEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option,
const QModelIndex &index)
{
Q_UNUSED(event);
Q_UNUSED(model);
if (index.isValid()) {
m_startIndex = index;
m_startPoint = { event->x() - option.rect.x(), option.rect.center().y() };
}
}
void LineDelegate::handleMouseMove(QMouseEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option,
const QModelIndex &index)
{
const QPoint endPoint = { event->x() - option.rect.x(), option.rect.center().y() };
if (m_startIndex != index) {
m_startIndex = index;
m_startPoint = endPoint;
}
const Line &line = Line::fromQLine({ m_startPoint, endPoint }, option.rect);
model->setData(index, QVariant::fromValue(line), Line::DataRole);
}
void LineDelegate::handleMouseRelease(QMouseEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option,
const QModelIndex &index)
{
Q_UNUSED(event);
Q_UNUSED(model);
Q_UNUSED(option);
Q_UNUSED(index);
m_startIndex = QModelIndex();
m_startPoint = { -1, -1 };
}
Вот как это выглядит:
Полный код приведенного выше примера доступен на github
Обратите внимание - это всего лишь краткий прототип. Отсутствует поддержка таких вещей, как движение мыши справа налево или ограничение только текущей строкой и т. Д. c. Надеюсь, этого будет достаточно для начала.