QTableView и взаимодействие с моделью данных

Замечания по стандартной реализации представления QTableView и модели QSqlRelationalTableModel.

Поработаем отладчиком , сделаем заходы в функции и посмотрим , что там делается ( такое становится возможно только при сборке qt из исходников).

QTableView

Теперь пора разобраться как QTableView отрисовывает ячейки таблицы. Для этого как обычно унаследуемся от QTableView и начнем переопределять виртуальные функции и потом отладчиком можно будет заглянуть что там внутри творится.

А творится там много чего и это не для слабонервных...

Сначала возникает какое либо событие от мышки ,клавиатуры и т.д. Значит надо искать входы этих событий в QTableView.

Но начинать надо с начала (не правда ли?..). А что есть начало?...

Допустим открыли мы QTableView , можно переопределить event() и посмотреть какие события приходят в QTableView (от ОС).

17:06:00 Debug: my_QTableView::event  "ChildPolished" 
17:06:00 Debug: my_QTableView::event  "ChildPolished" 
17:06:00 Debug: my_QTableView::event  "ChildPolished" 
17:06:00 Debug: my_QTableView::event  "ChildAdded" 
17:06:00 Debug: my_QTableView::event  "Polish" 
17:06:00 Debug: my_QTableView::event  "DynamicPropertyChange" 
17:06:00 Debug: my_QTableView::event  "FontChange" 
17:06:00 Debug: my_QTableView::event  "ChildPolished" 
17:06:00 Debug: my_QTableView::event  "ChildPolished" 
17:06:00 Debug: my_QTableView::event  "ChildPolished" 
17:06:00 Debug: my_QTableView::event  "Move" 
17:06:00 Debug: my_QTableView::event  "Resize" 
17:06:00 Debug: my_QTableView::event  "Show" 
17:06:00 Debug: my_QTableView::event  "ShowToParent" 
17:06:00 Debug: my_QTableView::event  "WindowActivate" 
17:06:00 Debug: my_QTableView::event  "PolishRequest" 
17:06:00 Debug: my_QTableView::event  "MetaCall" 
17:06:00 Debug: my_QTableView::event  "MetaCall" 
17:06:00 Debug: my_QTableView::event  "Timer" 
17:06:00 Debug: my_QTableView::event  "MetaCall" 
17:06:00 Debug: my_QTableView::event  "MetaCall" 
17:06:00 Debug: my_QTableView::event  "LayoutRequest" 
17:06:00 Debug: my_QTableView::event  "Paint" 
17:06:00 Debug: my_QTableView::event  "Timer" 
17:06:01 Debug: my_QTableView::event  "WindowDeactivate" 
17:06:01 Debug: my_QTableView::event  "Paint" 
17:06:01 Debug: my_QTableView::event  "Paint"

Далее как происходит интерактивное взаимодейтсвие? Щелкаем мышкой (левой кнопкой) на числовом поле, вбиваем цифру и нажимаем Enter, происходит выход из режима редактирования:

При перемещении мыши над QTableView будут возникать события Enter. Но сам обработчик Enter ничего не делает , только зачем-то посылает событие QStatusTipEvent в случае :

    case QEvent::Enter:
#ifndef QT_NO_STATUSTIP
        if (d->statusTip.size()) {
            QStatusTipEvent tip(d->statusTip);
            QApplication::sendEvent(const_cast(this), &tip);
        }
#endif
        enterEvent(event);
        break;

Далее прилетает FocusIn и попадаем мы после этого в обработчик:

Bool QAbstractItemView::event(QEvent *event)

Он проверяем свои типы событий и перекидывает в
bool QAbstractScrollArea::event(QEvent *e)
тут вроде проверяются свои типы событий и перекидывают дальше в :
bool QFrame::event(QEvent *e)
Далее в
bool QWidget::event(QEvent *event)
и наконец заходим в :
focusInEvent((QFocusEvent*)event);
но это уже почему-то в :
void QAbstractItemView::focusInEvent(QFocusEvent *event)

void QAbstractItemView::focusInEvent(QFocusEvent *event)
{
    Q_D(QAbstractItemView);
    QAbstractScrollArea::focusInEvent(event);

    const QItemSelectionModel* model = selectionModel();
    const bool currentIndexValid = currentIndex().isValid();

    if (model
        && !d->currentIndexSet
        && !currentIndexValid) {
        bool autoScroll = d->autoScroll;
        d->autoScroll = false;
        QModelIndex index = moveCursor(MoveNext, Qt::NoModifier); // first visible index
        if (index.isValid() && d->isIndexEnabled(index) && event->reason() != Qt::MouseFocusReason)
            selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate);
        d->autoScroll = autoScroll;
    }

    if (model && currentIndexValid) {
        if (currentIndex().flags() != Qt::ItemIsEditable)
            setAttribute(Qt::WA_InputMethodEnabled, false);
        else
            setAttribute(Qt::WA_InputMethodEnabled);
    }

    if (!currentIndexValid)
        setAttribute(Qt::WA_InputMethodEnabled, false);

    d->viewport->update();
}

QModelIndex index = moveCursor(MoveNext, Qt::NoModifier) - тут определяется текущий индекс.

Далее проверяется его Qt::ItemIsEditable и как результат - для виджета устанавливаем аттрибут Qt::WA_InputMethodEnabled:
setAttribute(Qt::WA_InputMethodEnabled, true);


QApplicationPrivate::setFocusWidget
void QWidget::setFocus(Qt::FocusReason reason)
bool QApplication::notify(QObject *receiver, QEvent *e)
   res = d->notify_helper(w, w == receiver ? mouse : &me);
bool QApplicationPrivate::notify_helper(QObject *receiver, QEvent * e)
    // send to all receiver event filters
    if (sendThroughObjectEventFilters(receiver, e))  !!!! вот здесь срабатывает indexAt
        return true;
bool QAbstractItemView::viewportEvent(QEvent *event)
bool QAbstractScrollArea::viewportEvent(QEvent *e)
void QAbstractItemView::mousePressEvent(QMouseEvent *event)
....
QPersistentModelIndex index = indexAt(pos);
...
    if (edit(index, NoEditTriggers, event))
        return;
...
bool QAbstractItemView::edit(const QModelIndex &index, EditTrigger trigger, QEvent *event)

 } else {
                    w->setAttribute(Qt::WA_NoMouseReplay, false);
                    res = d->notify_helper(w, w == receiver ? mouse : &me);
                    e->spont = false;
                }

KeyRelease
KeyPress

Изучаем QTableView::paintEvent(e) .

Сразу при открытие окна с QTableView начинается прорисовка всех ячеек таблицы. И это логично ведь само ничего не прорисуется.

После подготовительных операций вызывается drawCell для каждой видимой ячейки:
d->drawCell(&painter, option, index);
Это хорошо видно по index .

Далее после запроса флага Qt::ItemIsEnabled у модели данных выбирается соответсвующий вариант палитры. Также проверяется ниличие выделения по State_Selected. Проверяется QStyle::State_MouseOver над ячейкой. QStyle::State_HasFocus.

Отрисовка происходит по drawPrimitive. Тут указывается PrimitiveElement = PE_PanelItemViewRow.


    q->style()->drawPrimitive(QStyle::PE_PanelItemViewRow, &opt, painter, q);

    q->itemDelegate(index)->paint(painter, opt, index);

Внутри itemDelegate всегда происходит отрисовка :
style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, widget);

CE_ItemViewItem это тип элемента управления (enum ControlElement из QStyle).