QAbstractListModel и адаптер QList

В моем приложении хранится несколько объектов типа, который наследуется от QAbstractListModel объектов.

Это создает довольно много дублированного кода при переносе простого std::vector<T> или QList<T> в модель с общими функциями добавления, удаления и множественного выбора.

Предполагается ли так использовать QAbstractListModel или есть какой-то класс адаптера, который может удалить дублированный код (по крайней мере, для контейнеров, являющихся частью Qt)?

Пример: я хочу обернуть vector<ObjectA> и vector<ObjectB> в модель. Код для insertRows, deleteRows, columnCount и т. д. всегда будет одним и тем же, и я хотел бы объединить это (с небольшим метапрограммированием, которое могло бы работать даже с tuple и data).


person pmr    schedule 17.01.2012    source источник
comment
Вы имели в виду QAbstractListModel или QAbstractItemModel?   -  person Karlson    schedule 17.01.2012
comment
@Karlson Действительно, множество похожих имен в среде ModelView меня смутило. Я оставлю тег abstractitemmodel как основной тег для фреймворка.   -  person pmr    schedule 17.01.2012


Ответы (2)


Вы должны сделать это в двух отдельных классах, потому что расширения Qt для С++ (СИГНАЛЫ, СЛОТЫ и т. д.) плохо работают с шаблонами. Обоснование и обходной путь для этого можно найти по адресу: https://doc.qt.io/archives/qq/qq15-academic.html

Вот примерный план решения. (Это основано на коде, который мы используем в нашем приложении, и он работает нормально.)

<сильный>1. Класс абстрактного списка, выполняющий функции Qt

class FooListModelQt : public QAbstractTableModel {
  Q_OBJECT

public:
  // Non-template methods, signals, slots, etc. can be used here. For example...
  QSet<int> SelectedRows() const;
  // ... etc. ...

signals:
  void SelectionChanged();
  // ... etc. ...

protected:
  explicit FooListModelQt(QObject *parent = NULL);
  virtual ~FooListModelQt() = 0;
  // ... etc. ...

};

<сильный>2. Абстрактный класс, выполняющий работу с шаблонами

template <typename T>
class FooListModel : public FooListModelQt {
public:
  const T* at(int index) const { return items_.at(index); }
  int count() const { return items_.count(); }
  void Append(T *item);
  // ... etc. ...

protected:
  explicit FooListModel(QObject *parent = NULL);
  virtual ~FooListModel();

private:
  QList<T*> items_;
};

<сильный>3. Фактический класс списка

class BarListModel : public FooListModel<Bar> {
  Q_OBJECT

public:
  explicit BarListModel(QObject *parent = NULL);
  int columnCount(const QModelIndex &parent) const;
  QVariant data(const QModelIndex &index, int role) const;
  // ... etc. ...
};
person Dave Mateer    schedule 17.01.2012
comment
Итак, если я пропущу макрос Q_OBJECT, moc всегда будет игнорировать класс и позволит мне заниматься своими шаблонными делами? (ОТ Эта статья представляет собой просто извиняющийся текст и не дает никаких аргументов в пользу отказа от основных функций языка. Особенно перед лицом реализаций сигналов и слотов, которые не налагают этих ограничений.) - person pmr; 17.01.2012
comment
Это мое предположение, да. Если вы не используете ничего, что требует Q_OBJECT (сигналы, слоты, трансляция и т. д.), то все должно быть в порядке. - person Dave Mateer; 17.01.2012
comment
Я только что потерял на этом несколько часов, так что совет: если вы собираетесь выставлять окончательный объект модели в QML/декларативный графический интерфейс, убедитесь, что точный класс конкретной модели помечен Q_OBJECT. Он сам должен быть им отмечен, он не "наследуется" от базы. Видите BarListModel в примере Дэйва? Q_OBJECT повторяется. Также понадобится qmlRegisterType. Это означает, что ваш окончательный класс не может быть шаблоном, это должен быть простой класс, такой как BarListModel. - person quetzalcoatl; 22.09.2012

Обычно я реализовывал бы свою собственную модель, наследующую напрямую от QAbstractItemModel, и предоставлял бы собственную реализацию функций представления, таких как data(), для обработки контейнера хранения данных, который я даю модели.

Если у вас есть дублирование кода для использования QList<T> и std::vector<T>, я бы предложил преобразовать одно в другое, выполнив одно из следующих действий:

QList<T> list = QList::fromVector(QVector::fromStdVector(vector));

или наоборот.

std::vector<T> vector = qlist.toVector().toStdVector();

Я бы сделал последнее, но вы можете выбрать любой.

На основании ваших дополнительных комментариев есть 2 пути действий, которые вы можете предпринять:

Путь 1:

Реализуйте objectA и objectB следующим образом:

class objectA : baseObject

и

class objectB : baseObject

где baseObject это:

struct baseObject
{
    virtual std::string toString() = 0;
};

Вероятно, проще преобразовать в строку, чем во что-либо еще.

Путь 2 будет в основном включать внутри модели использование std::vector<boost::any>() в качестве контейнера для хранения данных, таким образом, вы можете реализовать подкласс одной модели QAbstractListModel.

То, что вы должны учитывать, это то, что если ваш контейнер для хранения данных вы, вероятно, можете сделать общим представление данных, вы ограничены тем, что вы можете сделать, потому что функция data(), которая даст ваше представление, элемент должен возвращать QVariant и он ограничен тем, что вы можете построить его из.

person Karlson    schedule 17.01.2012
comment
Суть в том, что базовый класс, реализующий шаблонную пластину, должен быть шаблоном. Я немного отшлифовал свой вопрос, чтобы лучше отразить мою проблему. - person pmr; 17.01.2012