Перехват событий QMouseEvent от иконки item'ов в QAbstractItemView

Материал из Wiki.crossplatform.ru

Перейти к: навигация, поиск

Иногда может потребоваться перехватывать события QMouseEvent от иконки ячейки таблицы или элемента дерева. К примеру, на рисунке изображено некое дерево подключений, и нужно по двойному нажатию по иконке (только по иконке) листа произвести подключение, а по двойному нажатию по иконке ветки подключить все листья (здесь важно то, что в листе по мимо иконки содержится еще и чекер).

Файл:Connect-manager.jpeg‎

Qt не предоставляет стандартных средств для перехвата подобных событий. Хотя могли бы, так как весь необходимый функционал для этого есть. Находится он в protected области класса QItemDelegate. Поэтому ничто не запрещает нам его использовать.

Для реализации требуемого нам функционала создадим собственную реализацию QItemDelegate и переопределим в ней метод editorEvent

class IconEventFilterDelegate : public QItemDelegate
{
    Q_OBJECT
    Q_DISABLE_COPY(IconEventFilterDelegate);
 
public:
    explicit IconEventFilterDelegate(QObject *parent = 0);
 
    virtual bool editorEvent(QEvent *event, QAbstractItemModel *model,
                       const QStyleOptionViewItem &option, const QModelIndex &index);
signals:
    void iconDoubleClicked(const QModelIndex &index);
};


Замечание Замечание: Класс использует не документированные возможности Qt.


IconEventFilterDelegate::IconEventFilterDelegate(QObject *parent) :
    QItemDelegate(parent)
{
}
 
bool IconEventFilterDelegate::editorEvent(QEvent *event, QAbstractItemModel *model,
                       const QStyleOptionViewItem &option, const QModelIndex &index)
{
    if (event->type() == QEvent::MouseButtonDblClick) {
        QMouseEvent *me = static_cast<QMouseEvent*>(event);
        if (me->button() == Qt::LeftButton ) {
            QPixmap pixmap;
            QRect decorationRect;
            QVariant value = index.data(Qt::DecorationRole);
 
            if (value.isValid()) {
                pixmap = decoration(option, value);
                decorationRect = QRect(QPoint(0, 0), pixmap.size());
 
                // Вичислим область занимаемую чекером. 
                // Этого можно и не делать, если у вас нет чекера. И в качестве checkRect передать emptyRect.  
                // Зато так код становится более общным, применимым к елементам как с чекером, так и без него
                QRect checkRect;
                Qt::ItemFlags flags = model->flags(index);
                if (flags.testFlag(Qt::ItemIsUserCheckable))
                    checkRect = check(option, option.rect, Qt::Checked);
 
                QRect emptyRect;
                doLayout(option, &checkRect, &decorationRect, &emptyRect, false);
 
                if (decorationRect.contains(me->pos())) {
                    emit iconDoubleClicked(index);
                    return true;
                }       
            }
        }
    }
    return QItemDelegate::editorEvent(event, model, option, index);
}

[править] Обсуждение

Обсудить на форуме...