Undefined reference to QAbstractListModel::postion - qt

I'm getting this error when implementing QAbstractListModel.
./debug\moc_ObjectModel.o:moc_ObjectModel.cpp:(.rdata$_ZTV12ObjectModel[__ZTV12ObjectModel]+0x38): undefined reference to `QAbstractListModel::position(int, int, QModelIndex const&) const'
collect2.exe: error: ld returned 1 exit status
Building the project was successful before I add insert and remove method to my model.
First, I add the insert and remove method but forgot to call begin* and end* method, so I got an error when rebuilding the project.
After that, I add begin* and end* as stated in the docs. But, when I build the project, I'm getting the error stated above. Then, I remove the insert and remove method and the error still remains.
I have tried to delete the build directory of the project, clean, run qmake, and then build the project but it was no good.
ObjectModel.h
#ifndef OBJECTMODEL_H
#define OBJECTMODEL_H
#include "ProjectCoreGlobal.h"
#include "Data/MyObject.h"
#include <QAbstractListModel>
class PROJECTCORESHARED_EXPORT ObjectModel : public QAbstractListModel
{
Q_OBJECT
public:
explicit ObjectModel(QObject *parent = 0);
int rowCount(const QModelIndex &parent) const Q_DECL_OVERRIDE;
QVariant data(const QModelIndex &position, int role) const Q_DECL_OVERRIDE;
// void insertObject(MyObject *object);
// void removeObject(int position);
MyObjectList objects() const;
MyObject *objectById(const int &id);
MyObject *objectByName(const QString &name);
private:
MyObjectList mObjects;
};
#endif // OBJECTMODEL_H
ObjectModel.cpp
#include "ObjectModel.h"
ObjectModel::ObjectModel(QObject *parent) :
QAbstractListModel(parent)
{
}
int ObjectModel::rowCount(const QModelIndex &parent) const
{
if (parent.isValid())
return 0;
else
return mObjects.size();
}
QVariant ObjectModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
MyObject *obj = mObjects.at(index.row());
if (role == Qt::DisplayRole) {
return obj->name();
}
return QVariant();
}
// void ObjectModel::insertObject(MyObject *object)
// {
// beginInsertRows(QModelIndex(), mObjects.count(), mObjects.count());
// mObjects.append(object);
// endInsertRows();
// }
// void ObjectModel::removeObject(int position)
// {
// beginRemoveRows(QModelIndex(), position, position);
// mObjects.removeAt(position);
// endRemoveRows();
// }
MyObjectList ObjectModel::objects() const
{
return mObjects;
}
MyObject *ObjectModel::objectById(const int &id)
{
foreach (MyObject *obj, mObjects)
{
if (obj->id() == id)
{
return obj;
}
}
return NULL;
}
MyObject *ObjectModel::objectByNme(const QString &name)
{
foreach (MyObject *obj, mObjects)
{
if (obj->name() == name)
{
return object;
}
}
return NULL;
}

The only QAbstractListModel class member that matches the signature from the error message is QAbstractListModel::index.
It most certainly means that there is a #define index position somewhere before the include for that class, either in the 2 headers files, or in the project settings (DEFINES += index=position in the .pro file).
Try putting #include <QAbstractListModel> first in ObjectModel.h.
You should also check that the Qt header file for the QAbstractListModel class wasn't inadvertently modified by a search/replace in the wrong window (compare with the original class here).

Related

Plugging a QSortFilterProxyModel on a QTableView unexpectdly crashed

I wrote a small Qt prototype which consists in a QTreeView on which I plugged QStandardItemModel. From there, I would like to be able to view the last level of my tree in a QTableView which I would like to be sortable and, why not, filterable. That seems to be the perfect scenario for the use of QSortFilterProxyModel. After implementation, the global view looks like what I want with the QTreeView on the left side and the QTableView on the right side(see image attached).
However, I face two issues:
only the first column is sortable
worse, if I select an item which is not in the first column and then click on one of the horizontal header the application crashes unexpectedly without any debugging backtrace in any of my implemented methods.
I may have misunderstood something regarding the use of Qt proxy model but do not know what and I can not find any clear hint on stack or elsewhere.
Here is my prototype cpp file:
#include <string>
#include <vector>
#include <QDebug>
#include <QAbstractProxyModel>
#include <QSortFilterProxyModel>
#include <QStandardItem>
#include <QStandardItemModel>
#include "mainwindow.h"
#include "ui_mainwindow.h"
struct Peak {
Peak(const std::string& name, int index, double qx, double qy, double qz);
std::string _name;
int _index;
double _qx;
double _qy;
double _qz;
};
Peak::Peak(const std::string& name, int index, double qx, double qy, double qz)
: _name(name),
_index(index),
_qx(qx),
_qy(qy),
_qz(qz)
{
}
struct PeakItem : public QStandardItem
{
PeakItem(const Peak& peak);
Peak _peak;
};
PeakItem::PeakItem(const Peak& peak)
: _peak(peak)
{
setText(QString::fromStdString(_peak._name));
appendRow(new QStandardItem(QString::number(_peak._index)));
appendRow(new QStandardItem(QString::number(_peak._qx)));
appendRow(new QStandardItem(QString::number(_peak._qy)));
appendRow(new QStandardItem(QString::number(_peak._qz)));
}
struct PeakListItem : public QStandardItem
{
PeakListItem(const std::vector<Peak>& peaks);
std::vector<Peak> _peaks;
};
PeakListItem::PeakListItem(const std::vector<Peak>& peaks)
: _peaks(peaks)
{
setText("Peaks");
setCheckable(true);
for (size_t r=0; r < _peaks.size(); ++r) {
auto item = new PeakItem(_peaks[r]);
appendRow(item);
}
}
struct PeakListProxyModel : public QSortFilterProxyModel {
PeakListProxyModel(PeakListItem* peak_list_item);
virtual QModelIndex mapFromSource(const QModelIndex &) const override;
virtual QModelIndex mapToSource(const QModelIndex &) const override;
virtual QModelIndex parent(const QModelIndex &) const override;
virtual QModelIndex index(int, int, const QModelIndex & p = QModelIndex()) const override;
virtual int rowCount(const QModelIndex & p = QModelIndex()) const override;
virtual int columnCount(const QModelIndex & p = QModelIndex()) const override;
virtual void sort(int column, Qt::SortOrder order) override;
PeakListItem* _peak_list_item;
};
PeakListProxyModel::PeakListProxyModel(PeakListItem* peak_list_item)
: _peak_list_item(peak_list_item)
{
}
QModelIndex PeakListProxyModel::parent(const QModelIndex &) const
{
return QModelIndex();
}
QModelIndex PeakListProxyModel::index(int row, int column, const QModelIndex& index) const
{
// qDebug()<<"create index "<<row<<" --- "<<column<<" --- "<<index;
return createIndex(row, column);
}
void PeakListProxyModel::sort(int column, Qt::SortOrder order)
{
qDebug()<<"sort "<<column;
QAbstractProxyModel::sort(column,order);
layoutChanged();
}
QModelIndex PeakListProxyModel::mapFromSource(const QModelIndex& source_index) const
{
if (!source_index.isValid()) {
return QModelIndex();
}
auto model = dynamic_cast<QStandardItemModel*>(sourceModel());
auto item = model->itemFromIndex(source_index);
auto parent_item = item->parent();
auto p = dynamic_cast<PeakItem*>(parent_item);
if (!p) {
return QModelIndex();
}
auto proxy_index = createIndex(parent_item->index().row(),item->index().row());
qDebug()<<"map from source --- "<<source_index<<" --- "<<proxy_index;
return proxy_index;
}
QModelIndex PeakListProxyModel::mapToSource(const QModelIndex& proxy_index) const
{
if (!proxy_index.isValid()) {
return QModelIndex();
}
auto peak_item = _peak_list_item->child(proxy_index.row());
auto prop_item = peak_item->child(proxy_index.column());
auto source_index = prop_item->index();
// qDebug()<<"map to source "<<proxy_index<<" --- "<<source_index<<" ---- "<<prop_item;
return source_index;
}
int PeakListProxyModel::rowCount(const QModelIndex& proxy_index) const
{
if (proxy_index.isValid()) {
return 0;
} else {
return _peak_list_item->rowCount();
}
}
int PeakListProxyModel::columnCount(const QModelIndex& proxy_index) const
{
if (proxy_index.isValid()) {
return 0;
} else {
return 4;
}
}
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QStandardItemModel* model = new QStandardItemModel();
QStandardItem* experiment = new QStandardItem("Experiment");
QStandardItem* peaks_item = new QStandardItem("Peaks");
std::vector<Peak> peaks;
peaks.reserve(4);
for (int i = 0; i < 4; ++i) {
peaks.emplace_back("Peak"+std::to_string(i),i,i,i,i);
}
// PeakListItem* peak_list_item1 = new PeakListItem(peaks);
// peaks_item->appendRow(peak_list_item1);
PeakListItem* peak_list_item2 = new PeakListItem(peaks);
peaks_item->appendRow(peak_list_item2);
experiment->appendRow(peaks_item);
model->appendRow(experiment);
ui->treeView->setModel(model);
PeakListProxyModel* proxy_model = new PeakListProxyModel(peak_list_item2);
proxy_model->setSourceModel(model);
ui->tableView->setModel(proxy_model);
ui->tableView->setSortingEnabled(true);
for (int i = 0; i < peaks.size(); ++i) {
// ui->treeView->setRowHidden(i,peak_list_item1->index(),true);
// ui->treeView->setRowHidden(i,peak_list_item2->index(),true);
}
}
MainWindow::~MainWindow()
{
delete ui;
}
After digging deeper I found that link
Basically explains how to solve my problem. To do the job, we have to pipe two models, one that do the mapping which derives from a QAbstractProxyModel and a second one that do the sorting/filetring which can be a standard QSortFilterProxyModel.

Extending QFileSystemModel by a bool variable

I would like to extend the QFileSystemModel. I would like to have a custom role IsSelectedRole where I can store whether a bool value which stores whether a file got selected in a QML TreeView. More precisely, I don't know how to set the setData and data function of my QFileSystemModel derived class, like where to store the data. I guess making my own tree of bool variables should work, but I hope that there is an easier way.
Qt already has a model that stores item selections: QItemSelectionModel. This model is used by the widget views as well as the QML views. All that's left for you to do is to overlay the data from that model on top of the data from QFileSystemModel, in a viewmodel.
You definitely shouldn't be deriving from QFileSystemModel. The viewmodel maintains the state for a single view only, and should be a proxy that overlays your role on top of the underlying model.
The proxy doesn't have to assume anything about the underlying model, and can work on any model, not just the QFileSystemModel.
For example:
// https://github.com/KubaO/stackoverflown/tree/master/questions/filesystem-model-select-50132273
#include <QtWidgets>
#include <algorithm>
class SelectionProxy : public QIdentityProxyModel {
Q_OBJECT
Q_PROPERTY(QItemSelectionModel* selectionModel
READ selectionModel WRITE setSelectionModel NOTIFY selectionModelChanged)
Q_PROPERTY(QVector<int> roles READ roles WRITE setRoles)
using self_t = SelectionProxy;
using base_t = QIdentityProxyModel;
using model_t = QItemSelectionModel;
QPointer<QItemSelectionModel> m_model;
QVector<QMetaObject::Connection> m_modelConnections;
QVector<int> m_roles{IsSelectedRole};
QModelIndex topLeft() const {
return sourceModel()->index(0, 0);
}
QModelIndex bottomRight() const {
return sourceModel()->index(sourceModel()->rowCount()-1, sourceModel()->columnCount()-1);
}
void onSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected) {
auto sel = selected;
sel.merge(deselected, QItemSelectionModel::Select);
for (auto &range : qAsConst(sel)) {
auto topLeft = mapFromSource(range.topLeft());
auto bottomRight = mapFromSource(range.bottomRight());
emit dataChanged(topLeft, bottomRight, m_roles);
}
}
void onModelChanged(QAbstractItemModel *model) {
setSourceModel(model);
}
bool check(const QModelIndex &index, int role) const {
return index.isValid() && m_model && m_roles.contains(role);
}
public:
static constexpr int IsSelectedRole = Qt::UserRole + 44;
SelectionProxy(QObject *parent = {}) : QIdentityProxyModel(parent) {}
QItemSelectionModel *selectionModel() const { return m_model; }
virtual void setSelectionModel(QItemSelectionModel *model) {
if (model == m_model) return;
for (auto &conn : m_modelConnections)
disconnect(conn);
m_model = model;
m_modelConnections = {
connect(m_model, &model_t::selectionChanged, this, &self_t::onSelectionChanged),
connect(m_model, &model_t::modelChanged, this, &self_t::onModelChanged) };
setSourceModel(model->model());
emit selectionModelChanged(m_model);
}
Q_SIGNAL void selectionModelChanged(QItemSelectionModel *);
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override {
if (!check(index, role))
return base_t::data(index, role);
return m_model->isSelected(mapToSource(index));
}
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override {
if (!check(index, role))
return base_t::setData(index, value, role);
using Sel = QItemSelectionModel;
m_model->select(mapToSource(index), value.toBool() ? Sel::SelectCurrent : (Sel::Deselect | Sel::Current));
return true;
}
QVector<int> roles() const { return m_roles; }
void setRoles(QVector<int> roles) {
std::sort(roles.begin(), roles.end());
if (roles == m_roles)
return;
std::swap(roles, m_roles);
if (!m_model)
return;
QVector<int> allRoles;
std::merge(roles.begin(), roles.end(), m_roles.begin(), m_roles.end(), std::back_inserter(allRoles));
emit dataChanged(topLeft(), bottomRight(), allRoles);
}
void setRole(int role) {
setRoles({role});
}
};
#include "main.moc"
A simple demonstrator sets up two views of the filesystem model; the bottom view shows true/false indicating the selection status of the first view.
int main(int argc, char **argv) {
QApplication app{argc, argv};
QWidget win;
QVBoxLayout layout{&win};
QTreeView left, right;
layout.addWidget(&left);
layout.addWidget(&right);
QFileSystemModel model;
SelectionProxy selProxy;
model.setRootPath(QStandardPaths::writableLocation(QStandardPaths::HomeLocation));
auto rootPathIndex = model.index(model.rootPath());
left.setModel(&model);
left.setSelectionMode(QAbstractItemView::MultiSelection);
left.setRootIndex(rootPathIndex);
selProxy.setRole(Qt::DisplayRole);
selProxy.setSelectionModel(left.selectionModel());
right.setModel(&selProxy);
right.setRootIndex(selProxy.mapFromSource(rootPathIndex));
for (int col : {1,2,3})
right.hideColumn(col);
win.show();
return app.exec();
}

QT table/model doesn't call data()

I'm trying to create a widget representing a table and update some data.
In order to do this I am following the Qt Model/View tutorial.
I've created classes (that you can find at the end of the post)
EmittersTableModel that inherits from QAbstractTableModel
EmittersTableWidget that inherits from QTableView
I havethe EmittersTableModel object as private member of EmittersTableWidget. In its constructor I instantiate the model and use the setModel() method.
Then, when I try to update data, I call the EmittersTableWidget::setRadioData() method, and I emit the datachanged() signal.
I've verified with the debugger that:
emit dataChanged(topLeft, bottomRight) is called
EmittersTableModel::rowCount() is called
EmittersTableModel::columnCount() is called
EmittersTableModel::flags() is never called
EmittersTableModel::data() is never called.
It seems for me that I'm doing all that tutorial says (use setModel(), implement needed virtual functions, emit the signal).
What I'm missing?
EmittersTableModel.h
#include <QAbstractTableModel>
#include <QVector>
#include "Radio.h"
typedef QMultiMap<QString, MapScenario::Core::RadioPtr> PlayerRadioMap;
class EmittersTableModel : public QAbstractTableModel
{
Q_OBJECT
public:
EmittersTableModel(QObject *parent);
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const ;
virtual int columnCount(const QModelIndex &parent = QModelIndex()) const;
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
virtual Qt::ItemFlags flags ( const QModelIndex & index ) const;
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
void setRadioData(const PlayerRadioMap &xRadioMap);
private:
typedef struct
{
QString xName;
MapScenario::Core::RadioPtr pxRadio;
} TableRowData;
PlayerRadioMap m_xRadioMap;
QVector<TableRowData> m_xDataVector;
};
EmittersTableModel.cpp
#include "EmittersTableModel.h"
EmittersTableModel::EmittersTableModel(QObject *parent)
:QAbstractTableModel(parent)
{
}
int EmittersTableModel::rowCount(const QModelIndex & /*parent*/) const
{
return m_xDataVector.size() - 1;
}
int EmittersTableModel::columnCount(const QModelIndex & /*parent*/) const
{
return 8;
}
QVariant EmittersTableModel::data(const QModelIndex &index, int role) const
{
if (role == Qt::DisplayRole)
{
switch (index.column())
{
case 0 :
{
return m_xDataVector.at(index.row()).xName;
} break;
case 1 :
{
return m_xDataVector.at(index.row()).pxRadio->getName();
} break;
}
return QString("Row%1, Column%2")
.arg(index.row() + 1)
.arg(index.column() +1);
}
return QVariant();
}
Qt::ItemFlags EmittersTableModel::flags(const QModelIndex &index) const
{
return Qt::ItemIsEnabled | Qt::ItemIsEditable;
}
QVariant EmittersTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role == Qt::DisplayRole)
{
if (orientation == Qt::Horizontal) {
switch (section)
{
case 0:
return QString("Player");
case 1:
return QString("Emitter");
case 2:
return QString("Freq.");
case 3:
return QString("Power");
case 4:
return QString("Modulation");
case 5:
return QString("Freq. Hopp.");
case 6:
return QString("Silent");
case 7:
return QString("Rec. Power");
}
}
}
return QVariant();
}
void EmittersTableModel::setRadioData(const PlayerRadioMap &xRadioMap)
{
m_xDataVector.clear();
PlayerRadioMap::const_iterator xIt;
for (xIt = xRadioMap.begin(); xIt != xRadioMap.end(); ++xIt)
{
TableRowData xData;
xData.xName = xIt.key();
xData.pxRadio = xIt.value();
m_xDataVector.append(xData);
}
if (false == m_xDataVector.empty())
{
QModelIndex topLeft = createIndex(0, 0);
QModelIndex bottomRight = createIndex(m_xDataVector.size() - 1, 7);
emit dataChanged(topLeft, bottomRight);
}
}
EmittersTableWidget.h
#include <QTableView>
#include <QHeaderView>
#include <QMultiMap>
#include <boost/smart_ptr.hpp>
#include "EmittersTableModel.h"
#include "Scenario.h"
#include "Radio.h"
namespace MapScenario
{
namespace Core
{
class Player;
}
}
/** Class for the player properties table model window */
class EmittersTableWidget : public QTableView
{
Q_OBJECT
public:
EmittersTableWidget(QWidget *xParent = 0);
~EmittersTableWidget();
public slots:
void refreshScenarioDataSlot(const MapScenario::Core::ScenarioPtr pxScenario);
private:
EmittersTableModel *m_pxModel;
void getTransmitterMap(const MapScenario::Core::ScenarioPtr pxScenario, PlayerRadioMap *pxRadioMap) const;
void sendDataToTableModel(const PlayerRadioMap &xRadioMap);
};
EmittersTableWidget.cpp
#include "EmittersTableWidget.h"
#include "Player.h"
#include "CoreException.h"
using MapScenario::Core::ScenarioPtr;
using MapScenario::Core::Radio;
using MapScenario::Core::PlayerPtr;
///////////////////////////////////////////////////////////////////////////////
// PUBLIC SECTION //
///////////////////////////////////////////////////////////////////////////////
EmittersTableWidget::EmittersTableWidget(QWidget *xParent)
: QTableView(xParent)
{
m_pxModel = new EmittersTableModel(0);
setModel(m_pxModel);
horizontalHeader()->setVisible(true);
verticalHeader()->setVisible(false);
setShowGrid(true);
setGridStyle(Qt::NoPen);
setCornerButtonEnabled(false);
setWordWrap(true);
setAlternatingRowColors(true);
setSelectionMode(QAbstractItemView::SingleSelection);
setSelectionBehavior(QAbstractItemView::SelectRows);
setSortingEnabled(true);
}
EmittersTableWidget::~EmittersTableWidget()
{
delete m_pxModel;
}
///////////////////////////////////////////////////////////////////////////////
// PUBLIC SLOTS SECTION //
///////////////////////////////////////////////////////////////////////////////
void EmittersTableWidget::refreshScenarioDataSlot(const ScenarioPtr pxScenario)
{
PlayerRadioMap xRadioMap;
getTransmitterMap(pxScenario, &xRadioMap);
sendDataToTableModel(xRadioMap);
}
void EmittersTableWidget::getTransmitterMap(const ScenarioPtr pxScenario, PlayerRadioMap *pxRadioMap) const
{
QVector<QString> xNameList;
QVector<QString>::const_iterator xNameIt;
QStringList::const_iterator xRadioIt;
pxScenario->getPlayersNameList(xNameList);
for (xNameIt = xNameList.begin(); xNameIt != xNameList.end(); ++xNameIt)
{
QStringList xRadioList;
PlayerPtr pxPlayer = pxScenario->getPlayer(*xNameIt);
pxPlayer->getRadioNameList(xRadioList);
for (xRadioIt = xRadioList.begin(); xRadioIt != xRadioList.end(); ++xRadioIt)
{
pxRadioMap->insert(pxPlayer->getName(), pxPlayer->getRadio(*xRadioIt));
}
}
}
void EmittersTableWidget::sendDataToTableModel(const PlayerRadioMap &xRadioMap)
{
m_pxModel->setRadioData(xRadioMap);
}
I've found my problem.
In the tutorial that I've seen it was supposed that row and column numbers are Always constant. Instead I start with zero rows, and then I add or remove them when I need. In order to do this, I need to re-implement following methods:
virtual bool insertRows(int row, int count, const QModelIndex &parent)
virtual bool insertColumns(int column, int count, const QModelIndex &parent)
virtual bool removeRows(int row, int count, const QModelIndex &parent)
virtual bool removeColumns(int column, int count, const QModelIndex &parent)
as explained in QAbstractItemModel page in subclassing section. Now I insert and remove rows when needed and table is updated correctly.

base class 'QAbstractListModel' has private copy constructor

I have a QT QML project. (still very small)
I started by binding a listview on my UScenario model, by subclassing QAbstractListModel and it worked fined.
Now, each UScenario has a list of UTask, which also have a list of UCondition (so, Utask also subclasses QAbstractListModel). But then, QT Creator gives me an error:
Core/Tasks/utask.h:6: erreur : base class 'QAbstractListModel' has private copy constructor
class UTask: public QAbstractListModel
^
So I'm not sure where is my problem. I tried reading the doc about QAbstractListModel vs QAbstractItemModel, but I have no clue.
I also tried to see if I ever constructed a UTask in a wrong way; I think not.
// USCENARIO.h
#ifndef USCENARIO_H
#define USCENARIO_H
#include <QAbstractListModel>
#include "../Tasks/utask.h"
class UScenario : public QAbstractListModel
{
Q_OBJECT
public slots:
void cppSlot() { // Used to test the insertion from UI
this->addTask(UTask());
}
public:
enum TaskRoles {
IdRole = Qt::UserRole + 1
};
UScenario(QObject *parent = 0);
private:
QList<UTask> m_tasks;
public:
void addTask(const UTask &task);
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
virtual QVariant data(const QModelIndex &index, int role) const;
virtual QHash<int, QByteArray> roleNames() const;
};
#endif // USCENARIO_H
// USCENARIO.CPP
#include "uscenario.h"
UScenario::UScenario(QObject *parent)
: QAbstractListModel(parent)
{
}
void UScenario::addTask(const UTask &task)
{
beginInsertRows(QModelIndex(), rowCount(), rowCount());
m_tasks.append(task);
endInsertRows();
}
int UScenario::rowCount(const QModelIndex & parent) const {
return m_tasks.count();
}
QVariant UScenario::data(const QModelIndex & index, int role) const {
if (index.row() < 0 || index.row() >= m_tasks.count())
return QVariant();
const UTask &task = m_tasks[index.row()];
if (role == IdRole)
return task.id();
return QVariant();
}
QHash<int, QByteArray> UScenario::roleNames() const {
QHash<int, QByteArray> roles;
roles[IdRole] = "id";
return roles;
}
// UTASK.H
#ifndef UTASK_H
#define UTASK_H
#include <QAbstractListModel>
#include "../Conditions/ucondition.h"
class UTask: public QAbstractListModel
{
Q_OBJECT
public:
enum TaskRoles {
typeRole = Qt::UserRole + 1
};
UTask(QObject *parent = 0);//:m_id(0){}
int id() const{return m_id;}
private:
int m_id;
QList<UCondition> m_conditions;
// QAbstractItemModel interface
public:
void addCondition(const UCondition &cond);
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
virtual QVariant data(const QModelIndex &index, int role) const;
virtual QHash<int, QByteArray> roleNames() const;
};
#endif // UTASK_H
// UTASK.cpp
#include "utask.h"
UTask::UTask(QObject *parent):
QAbstractListModel(parent), m_id(0)
{
}
void UTask::addCondition(const UCondition &cond)
{
beginInsertRows(QModelIndex(), rowCount(), rowCount());
m_conditions.append(cond);
endInsertRows();
}
int UTask::rowCount(const QModelIndex &parent) const
{
return m_conditions.count();
}
QVariant UTask::data(const QModelIndex &index, int role) const
{
if (index.row() < 0 || index.row() >= m_conditions.count())
return QVariant();
const UCondition &cond = m_conditions[index.row()];
if (role == typeRole)
return cond.type();
return QVariant();
}
QHash<int, QByteArray> UTask::roleNames() const
{
QHash<int, QByteArray> roles;
roles[typeRole] = "type";
return roles;
}
// MAIN
#include <QtGui/QGuiApplication>
#include "qtquick2applicationviewer.h"
#include <qqmlengine.h>
#include <qqmlcontext.h>
#include <qqml.h>
#include <QtQuick/qquickitem.h>
#include <QtQuick/qquickview.h>
#include "../uCtrlCore/Scenario/uscenario.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
UScenario scenarioModel;
scenarioModel.addTask(UTask());
scenarioModel.addTask(UTask());
scenarioModel.addTask(UTask());
QtQuick2ApplicationViewer viewer;
QQmlContext *ctxt = viewer.rootContext();
ctxt->setContextProperty("myScenarioModel", &scenarioModel);
viewer.setMainQmlFile(QStringLiteral("qml/uCtrlDesktopQml/main.qml"));
QObject *item = viewer.rootObject()->findChild<QObject*>("btn");
QObject::connect(item, SIGNAL(qmlSignal()), &scenarioModel, SLOT(cppSlot()));
viewer.showExpanded();
return app.exec();
}
There problem is with how you're storing the UTask objects in your UScenario class
QList<UTask> m_tasks
In simple terms when you call m_tasks.append it is attempting to allocate a new UTask object in the QList by copying the source UTask object via the default copy constructor. In the case of QAbstractListModel it is private. This is why you're at getting the error.
A simply solution is to change the storage type to a list of UTask pointers, QList< UTask* > along with the supporting code to properly release the memory when your UScenario object is destroyed.
For example here are some but not all of the changes but should point you in the right direction. Just make sure to change m_tasks to QList< UTask* > first:
int main(int argc, char *argv[])
{
...
UScenario scenarioModel;
scenarioModel.addTask( new UTask() );
scenarioModel.addTask( new UTask() );
scenarioModel.addTask( new UTask() );
...
return app.exec();
}
void UScenario::cppSlot()
{
// Used to test the insertion from UI
this->addTask( new UTask() );
}
// Change the signature to take a pointer
void UScenario::addTask( UTask* task )
{
beginInsertRows(QModelIndex(), rowCount(), rowCount());
m_tasks.append(task);
endInsertRows();
}
// Make sure you define a destructor for UScenario
UScenario::~UScenario()
{
QList< UTask* >::iterator task = m_tasks.begin();
while( m_tasks.end() != task )
{
// Release the memory associated with the task.
delete (*task);
++task;
}
m_tasks.clear();
}

Qt Model View for a given populated ro QList

i got a godgiven list of xyz (the code says int, just an example) glued into a QList (to big to move anywhere). How can I create a Model View for that? I allready read the Qt doc which tells me, I have to reimplement data, index, parent, rowCount, columnCount functions. But the preprocessor/compiler cries for more reimplemented functions? I allready read a chapter in my Qt Book but it did not help either. Here my hacked away code:
class XModel : public QAbstractListModel
{
Q_OBJECT
public:
explicit XModel(QList<int> *valuelist, QObject *parent = 0);
virtual ~XModel();
int rowCount(const QModelIndex &) const;
int columnCount(const QModelIndex &) const;
QModelIndex index( int row, int column, const QModelIndex & parent = QModelIndex()) const;
QModelIndex parent(const QModelIndex &index) const;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
private:
QList<int>* blah;
signals:
public slots:
};
XModel::XModel(QList<int> *valuelist, QObject *parent) :
QAbstractListModel(parent),
blah(valuelist)
{
}
XModel::~XModel()
{
}
int XModel::rowCount(const QModelIndex &) const
{
return blah->size();
}
int XModel::columnCount(const QModelIndex &) const
{
return 1;
}
QModelIndex XModel::index(int row, int column, const QModelIndex &parent) const
{
return createIndex(row, column, (void)&(blah->at(row)));
}
QModelIndex XModel::parent(const QModelIndex &index) const
{
return createIndex(index->row(), index->column(), NULL);
}
QVariant XModel::data(const QModelIndex &index, int role = Qt::DisplayRole) const
{
return QVariant(blah->at(index.row()));
}
Do I even have to use QAbstractItemModel or does QAbstractListModel work the exact same way? How do I give the Model the source of the actual data? Is this only within data function? Please tell me what I am doing wrong, I do not see it and give advice on howto do it properly, (good) howtos welcome.
This is fixed, but...
EDIT:
Widget::Widget(QWidget *parent)
: QWidget(parent),
valuelist(),
xm(&valuelist) //xm = XModel
{
valuelist.append(1);
valuelist.append(2);
valuelist.append(3);
valuelist.append(4);
valuelist.append(5);
valuelist.append(6);
valuelist.append(7);
valuelist.append(8);
valuelist.append(9);
view = new QListView(this);
view->setModel(&xm);
//how to force the XModel to reread the QList`?
view->show();
}
Add to and remove data from XModel and have XModel modify the underlying list (reference?) for you:
Widget::Widget(QWidget *parent)
: QWidget(parent),
valuelist(),
xm(&valuelist) //xm = XModel
{
xm.append(1);
xm.append(2);
xm.append(3);
xm.append(4);
xm.append(5);
xm.append(6);
xm.append(7);
xm.append(8);
xm.append(9);
view = new QListView(this);
view->setModel(&xm);
xm.append(10); // should call beginInsertRows, append to valuelist, and call endInsertRows.
Q_ASSERT(valuelist.contains(10));
view->show();
}
Otherwise, you could perhaps create a mix of QObject and QList that can emit signals to notify XModel of changes, but I think the first approach is better.
Edit:
Here is a silly example. It will create a list-backed model that will append more elements to the list every second:
#include <QtGui/QApplication>
#include <QtGui/QListView>
#include <QtCore/QAbstractListModel>
#include <QtCore/QTimer>
class ListBackedModel : public QAbstractListModel
{
Q_OBJECT
QList<int>* m_list;
public:
ListBackedModel(QList<int>* list, QObject* parent = 0)
: QAbstractListModel(parent)
, m_list(list)
{}
~ListBackedModel()
{}
int rowCount(const QModelIndex &parent = QModelIndex()) const
{
Q_UNUSED(parent);
return m_list->size();
}
QVariant data(const QModelIndex &index, int role) const
{
if (index.row() >= rowCount()) { return QVariant(); }
if (index.row() < 0) { return QVariant(); }
int element = m_list->at(index.row());
if (Qt::DisplayRole == role) {
return QString::number(element);
}
if (Qt::ToolTipRole == role) {
return tr("%1 is element #%2").arg(element).arg(index.row() + 1);
}
return QVariant();
}
void append(int element)
{
/*
First is the new index of the first element that will be inserted.
Last is the new index of the last element that will be inserted.
Since we're appending only one element at the end of the list, the
index of the first and last elements is the same, and is equal to
the current size of the list.
*/
int first = m_list->size();
int last = first;
beginInsertRows(QModelIndex(), first, last);
m_list->append(element);
endInsertRows();
}
void startAddingMoreElements()
{
QTimer::singleShot(1000, this, SLOT(addMoreElements()));
}
private slots:
void addMoreElements()
{
append(qrand() % 100);
startAddingMoreElements();
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QList<int> list;
list << 1 << 10 << 34 << 111;
ListBackedModel model(&list);
QListView listView;
listView.setModel(&model);
listView.show();
model.startAddingMoreElements();
return a.exec();
}
#include "main.moc"

Resources