Специализация QAbstractProxyModel для добавления столбца: ячейки таблицы становятся пустыми

Я создал прокси-модель, подобную миксину (Qt5), которая просто добавляет дополнительный первый столбец в другую прокси-модель для добавления QToolBar действий в каждую строку табличного представления (например, кнопку «удалить»). Модель просто обеспечивает способ заполнения QList<QVariant> для первого столбца. Делегат должен знать, что означает каждое QVariant (обычно ints/enums идентифицирующих действия), и соответственно заполнить QToolBar. В качестве последней функции, если нет действий, дополнительный столбец не добавляется (в этом случае он ведет себя как QIdentityProxyModel). После добавления действия нельзя удалить. Это функция для другого дня.

Сегодняшняя проблема заключается в том, что когда я вставляю действия (что я делаю перед установкой модели в представление), все ячейки остаются пустыми. Итак, я что-то делаю не так с сигналами или черт знает с чем (думаю, ошибка в функции add_action, в конце фрагмента):

template<class proxy_model>
class action_model : public proxy_model
{
    QList<QVariant> l_actions;

public:
    using base_t = proxy_model;
    using base_t::base_t; // Inheriting constructors.

    QModelIndex mapFromSource(const QModelIndex& source_idx) const override
    {
         if (!l_actions.empty() and source_idx.isValid())
            return this->createIndex(source_idx.row(),
                                     source_idx.column() + 1);
         else // identity proxy case
            return base_t::mapFromSource(source_idx);
    } // same for mapToSource but with - 1 instead of + 1.

    int columnCount(const QModelIndex& parent = QModelIndex()) const override
    { return this->base_t::columnCount() + !l_actions.empty(); }

    QVariant headerData(int section, Qt::Orientation orientation, int role) const override
    {
         if (!l_actions.empty()) {

            if (orientation == Qt::Horizontal and section == 0
                and role == Qt::DisplayRole)
                return "Actions"; // Testing.
            else
                return base_t::headerData(section - 1, orientation, role);

         } else // identity proxy case
            return base_t::headerData(section, orientation, role);
    }

    QVariant data(const QModelIndex& idx, int role) const override
    {
        if (!l_actions.empty()) {
            if (idx.column() == 0 and role = Qt::DisplayRole)
                return l_actions; // All the actions for drawing.
            else
                return QVariant();
        } else // identity proxy case
             return base_t::data(idx, role);
    }

    Qt::ItemFlags flags(QModelIndex const& idx) const
    {
        if (!l_actions.empty() and idx.column() == 0)
            return Qt::NoItemFlags; // No editable or selectable
        else
            return base_t::flags(idx);
    }

    // And here, I think, is where the fun starts:
    // The action could be added before or after the sourceModel
    // is set or this model is connected to a view, but I don't
    // how that cases are supposed to be managed.
    void add_action(QVariant const& action)
    {
         bool was_empty = l_actions.empty();
         l_actions << action;

         if (was_empty and !this->insertColumns(0, 1))
            throw std::logic_error("Something went wrong");

         Q_EMIT this->dataChanged
             (this->createIndex(0, 0),
              this->createIndex(this->rowCount(), 0),
              { Qt::DisplayRole });
    }

};

Без настройки действий модель работает нормально, как с QAbstractIdentityProxyModel, так и с QSortFilterProxyModel как proxy_model. Но при настройке действий представление показывает каждую пустую ячейку, как с QSortFilterProxyModel, так и с QAbstractIdentityProxyModel.

Вот код пользователя:

enum sql_action { DELETE };

auto* table_model = /* My QSqlTableModel */;
auto* view_model = new action_model<QIdentityProxyModel>(my_parent);
auto* table_view = new QTableView;

view_model->add_action(static_cast<int>(sql_action::DELETE));
view_model->setSourceModel(table_model);

table_view->setModel(view_model);
table_view->setSortingEnabled(true);
table_view->setAlternatingRowColors(true);
// The last column is printed in white, not with alternate colors.

table_view->show();
table_model->select();

Делегаты не проблема, потому что я никого не назначал. Я ожидаю первый столбец с белыми ячейками, но получаю полностью белую таблицу. Имена столбцов отображаются нормально, за исключением последнего, который печатает только 0 в качестве имени столбца.

Что я делаю не так?


person Peregring-lk    schedule 06.12.2016    source источник
comment
Возможно, недостающий mapToSource(). Код в columnCount() также не идеален, вы полагаетесь на то, что true равно 1, а не какое-либо другое ненулевое значение.   -  person Kevin Krammer    schedule 06.12.2016
comment
mapToSource закомментирован для сокращения вопроса, чуть ниже mapFromSource (поскольку они практически идентичны). columnCount укорачивается по той же причине. В моем коде я использую if без этого преобразования. В любом случае, emtpy возвращает bool, а стандарт гарантирует, что логическое значение false приводится к 0 int, а 1 к true. Итак, я думаю, что код правильный, соответствует стандарту и так далее. В любом случае проблема должна быть где-то в другом.   -  person Peregring-lk    schedule 06.12.2016
comment
Хм, конечно, я просто подумал, что было бы странно добавлять логическое значение к целому числу.   -  person Kevin Krammer    schedule 07.12.2016
comment
У меня похожая проблема, у вас в итоге получилось это сделать?   -  person brembo    schedule 28.11.2017
comment
@brembo Я решил это, но не помню как. Думаю, я решил это в соответствии с предложениями ответа Кевина Краммера ниже, но если я не отметил его решение как ответ, это потому, что под капотом было что-то более сложное, что я пропустил, что-то, что зависит от других вещей моего кода или может быть, я пробовал тонны мыслей, и я не был уверен, как я это решил.   -  person Peregring-lk    schedule 28.11.2017
comment
@Peregring-lk спасибо! Мне потребовалось некоторое время, но мне удалось заставить его работать. Оказывается, необходимо перезаписать несколько других методов (среди прочего, index() и parent()). Я опубликую свой подход позже, но сначала он нуждается в некоторой очистке.   -  person brembo    schedule 13.12.2017


Ответы (1)


Проблема в вашем методе data().

  1. Вы не сравниваете role с Qt::DisplayRole ролью, которую вы назначаете
  2. Если у вас есть действия, вы либо возвращаете запись действия, либо QVariant(), а не какие-либо данные.
person Kevin Krammer    schedule 07.12.2016