4

I want-to put-a customed-widget into the cell of QTableView. The Widget can-be a QPushButton, a checkBox or something else. I've tried following methods, but no-one satisfies me:

  1. Use delegate. This strategy can paint widget, but cant-be a customed widget, and cant interact.
  2. Use QTableView::setIndexWidget(). Using this strategy, the customed-widget covers the cell wholly & absolutely, delegate binded to QTableView not working, which means a-double-click() wont make Edit operation on-the cell.

Note that the function is stand-alone, like a plugin. So I cant do following things.

  1. Inherit from QTableView. I can only get a-pointer-variable binded with QTableView----only an object of QTableView.
  2. Inherit from any-model. The model is control by other user, the programmer writing model shouldn't and can't just use my-customed model.

The problem is really complex and too-many constraints restrict the design.

4
  • I think you will have to go the delegate way. You can use custom widgets and you can interact with them. See: stackoverflow.com/questions/16660292/…
    – Taron
    Commented Aug 4, 2017 at 6:51
  • the QItemDelegate::createEditor() just paints when the cell is-clicked. The need is that the widget can be shown previously, If I click the cell, another delegate also can work.But QItemDelegate::paint() only draw, not gives a real Widget that can-be interact
    – Nick.Rhan
    Commented Aug 4, 2017 at 7:35
  • tableView->openPersistentEditor( index ). With that its always visible!
    – Taron
    Commented Aug 4, 2017 at 8:22
  • still dosent work
    – Nick.Rhan
    Commented Aug 4, 2017 at 9:48

3 Answers 3

2

You can't do this without a delegate if you want to use model/view to contain the data of your widget. Even worse, if you try to do this anyway, you'll significantly slow down your program when the number of elements is very high, because every widget will be involved in the event loop. This is NOT what you want. You want to involve only the widgets that are visible to the user.

Considering reading this for more explanation.

1

Thanks for all of you contributing to this question, I've found a way to solve this problem. It is solved as the "original thought", which is just to draw a QToolButton on the tableView, using the QModelIndex. Through QModelIndex, I can get the geomertry location where I can draw any QWidget. However, this method is really complex, for I have to maintain the changes of QModelIndex, like removeColumns and insertColumns.

Another problem is concerned that I have to distinguish between hide & delete columns or rows. And I think I should post an another new question for help. Thanks again.

0

You should subclass the QTableView and also QItemDelegate:

In the table view, you should open persistent editor for all cells in the visible area and update theme accordingly:

table-view.h

#ifndef TABLE_VIEW_H
#define TABLE_VIEW_H

#include <QTableView>

class TableView : public QTableView
{
    Q_OBJECT

public:
    TableView(QWidget* parent = nullptr);

    void setModel(QAbstractItemModel* model) override;
    void scrollContentsBy(int dx, int dy) override;
    void scrollTo(const QModelIndex& index, ScrollHint hint) override;

protected slots:
    void resizeEvent(QResizeEvent* event) override;
    void dataChanged(const QModelIndex&  topLeft,
                     const QModelIndex&  bottomRight,
                     const QVector<int>& roles) override;
    void rowsInserted(const QModelIndex& parent, int start, int end) override;

private:
    void showAllVisiblePersistentEditors();
    QModelIndexList m_editor_list;
};

#endif // TABLE_VIEW_H

table-view.cpp

TableView::TableView(QWidget* parent) :
    QTableView(parent)
{
    setItemDelegate(new Delegate);
}

void TableView::setModel(QAbstractItemModel* model)
{
    QTableView::setModel(model);
    executeDelayedItemsLayout();
    showAllVisiblePersistentEditors();
}

void TableView::scrollContentsBy(int dx, int dy)
{
    QTableView::scrollContentsBy(dx, dy);
    showAllVisiblePersistentEditors();
}

void TableView::scrollTo(const QModelIndex& index, ScrollHint hint)
{
    QTableView::scrollTo(index, hint);
    showAllVisiblePersistentEditors();
}

void TableView::resizeEvent(QResizeEvent* event)
{
    QTableView::resizeEvent(event);
    executeDelayedItemsLayout();
    showAllVisiblePersistentEditors();
}

void TableView::dataChanged(const QModelIndex&  topLeft,
                            const QModelIndex&  bottomRight,
                            const QVector<int>& roles)
{
    QTableView::dataChanged(topLeft, bottomRight, roles);
    showAllVisiblePersistentEditors();
}

void TableView::rowsInserted(const QModelIndex& parent, int start, int end)
{
    QTableView::rowsInserted(parent, start, end);
    showAllVisiblePersistentEditors();
}

void TableView::showAllVisiblePersistentEditors()
{
    const auto* model { this->model() };

    if (model) {
        const auto rect { viewport()->rect() };

        const auto first_index { indexAt(rect.topLeft()) };

        const auto valid_bottom_right {
            visualRect(model->index(model->rowCount() - 1, model->columnCount() - 1))
            .bottomRight() };
        const auto bottom_right { rect.bottomRight() };
        const auto last_index { indexAt({ std::min(bottom_right.x(), valid_bottom_right.x()),
                                          std::min(bottom_right.y(), valid_bottom_right.y()) }) };

        const auto r1 { first_index.row() };
        const auto r2 { last_index.row() };
        const auto c1 { first_index.column() };
        const auto c2 { last_index.column() };

        QModelIndex index;

        const auto editor_count { m_editor_list.size() };
        for (auto i { editor_count - 1 }; i >= 0; --i) {
            index = qAsConst(m_editor_list)[i];
            if (!visualRect(index).intersects(rect)) {
                closePersistentEditor(index);
                m_editor_list.removeLast();
            }
        }

        for (auto r { r1 }; r <= r2; ++r) {
            for (auto c { c1 }; c <= c2; ++c) {
                index = model->index(r, c);
                if (index.flags().testFlag(Qt::ItemIsEditable) &&
                    visualRect(index).intersects(rect) &&
                    !isPersistentEditorOpen(index))
                {
                    m_editor_list.push_back(index);
                    openPersistentEditor(index);
                }
            }
        }
    }
}

In the delegate you can provide any widget you like for the editor:

delegate.h

#ifndef DELEGATE_H
#define DELEGATE_H

#include <QAbstractItemDelegate>

class Delegate : public QAbstractItemDelegate
{
    Q_OBJECT

public:
    Delegate(QObject* parent = nullptr);

    void paint(QPainter*                   painter,
               const QStyleOptionViewItem& option,
               const QModelIndex&          index) const override;

    QSize sizeHint(const QStyleOptionViewItem& option,
                   const QModelIndex&          index) const override;

    QWidget* createEditor(QWidget*                    parent,
                          const QStyleOptionViewItem& option,
                          const QModelIndex&          index) const override;

    void updateEditorGeometry(QWidget*                    editor,
                              const QStyleOptionViewItem& option,
                              const QModelIndex&          index) const override;
};

#endif // DELEGATE_H

delegate.cpp

#include "delegate.h"

#include <QPushButton>

Delegate::Delegate(QObject* parent) :
    QAbstractItemDelegate(parent)
{}

void Delegate::paint(QPainter*                   painter,
                     const QStyleOptionViewItem& option,
                     const QModelIndex&          index) const
{
    Q_UNUSED(painter)
    Q_UNUSED(option)
    Q_UNUSED(index)
}

QSize Delegate::sizeHint(const QStyleOptionViewItem& option,
                         const QModelIndex&          index) const
{
    Q_UNUSED(index)
    return option.decorationSize;
}

QWidget* Delegate::createEditor(QWidget*                    parent,
                                const QStyleOptionViewItem& option,
                                const QModelIndex&          index) const
{
    Q_UNUSED(option)
    Q_UNUSED(index)
    return new QPushButton(parent);
}

void Delegate::updateEditorGeometry(QWidget*                    editor,
                                    const QStyleOptionViewItem& option,
                                    const QModelIndex&          index) const
{
    Q_UNUSED(index)
    editor->setGeometry(option.rect);
}

Not the answer you're looking for? Browse other questions tagged or ask your own question.