QAbstractTableModel inheritance vtable problem - c++

Here's another problem with qt:
I extend a QAbstractTableModel, but I get a compiling error ( I'm using cmake)
// file.h
#ifndef TABLEMODEL_H
#define TABLEMODEL_H
#include <QAbstractTableModel>
class TableModel : public QAbstractTableModel
{
Q_OBJECT
public:
TableModel(QObject *parent = 0);
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
};
#endif
// file.c
#include "tableModel.h"
TableModel::TableModel(QObject *parent)
: QAbstractTableModel(parent){}
int TableModel::rowCount(const QModelIndex & ) const
{ return 1; }
int TableModel::columnCount(const QModelIndex & ) const
{ return 1;}
when I compile I get:
In function TableModel':
/partd/unusedsvn/unusedpkg/iface/tableModel.cpp:4: undefined reference tovtable for TableModel'
/partd/unusedsvn/unusedpkg/iface/tableModel.cpp:4: undefined reference to vtable for TableModel'
collect2: ld returned 1 exit status
does anybody got the same trouble??

Make sure you're running your header through MOC, and are linking those MOC object files.

Solved adding to CMakeLists.txt the needed cpp file.
set(tutorial_SRCS app.cpp mainWin.cpp tableModel.cpp)
When I'll run cmake, the moc* will be automatically created

Almost 100% percent of vtable errors are caused by either missing headers/class definitions or by typoes in those definitions, so the first thing to do is to make sure you got the headers and sources right (and included in project). I've personally cursed Qt to the lowest hell for that and missed that tiny typo in project file, not fun :)

Yes, vtable errors are a bitch.
You have to implement the code() method which is a pure virtual method too.
From the QAbstractTableModel documentation :
Subclassing
When subclassing QAbstractTableModel, you must implement rowCount(), columnCount(), and data().
I'm having a vtable problem too, and I implemented data(), so I'm missing other virtual crap but I don't know whitch one.

This is a fairly common bug when an object isn't moc'ed. I'd read the whole debugging document to save yourself some time down the road.

To resolve this problem, i've remove Q_OBJECT from TableModel, made new class TableModelController, that derived from QObject and have TableModel inside
class TableModel : public QAbstractTableModel
{
public:
TableModel(QObject *parent = 0);
// Some overrided functions
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
};
class TableModelController : public QObject
{
Q_OBJECT
public:
explicit TableModelController(QObject *parent = nullptr);
TableModelController(TableModel *m, QObject *parent = nullptr);
TableModel *getModel() {
return model;
}
public slots:
void addRow();
void deleteRows();
private:
TableModel *model;
};
Then i use TableModelController to access TableModel throw get Methond and public slots. I'm use QtCreator

Related

How do I interact with a model in an extended QQuickItem?

There are many good resources on dealing with models and views in the Qt doc, like: http://doc.qt.io/qt-5/model-view-programming.html, but I can't seem to find any that link to dealing with models in QtQuick. There are some basic chapters on extending qml with c++, like in http://doc.qt.io/qt-5/qtqml-tutorials-extending-qml-example.html, and on using models: http://doc-snapshots.qt.io/qt5-5.11/qtquick-modelviewsdata-modelview.html, but I can't quite find a way to use an actual model in extended qml.
Currently I have this model:
class LayoutModel : public QAbstractItemModel {
Q_OBJECT
public:
explicit LayoutModel(const QString &data, QObject *parent = 0);
~LayoutModel();
QVariant data(const QModelIndex &index, int role) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &index) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
private:
void setupModelData(const QStringList &lines, LayoutItem *parent);
LayoutItem *rootItem;
};
And I'm trying to access it from this view class:
class Layout : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(LayoutModel model READ model WRITE setModel NOTIFY modelChanged)
private:
LayoutModel & m_model;
public:
explicit Layout(QQuickItem * parent = nullptr);
LayoutModel & model() const;
void setModel(const LayoutModel & model);
void itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData & value) override;
void geometryChanged(const QRectF & newGeometry, const QRectF & oldGeometry) override;
signals:
void modelChanged();
};
But I can't find a way to actual use the model. I can't even properly set up read and write to the model, since QAbstractItemModels (and models in Qt in general) have their copy constructor deleted to enforce entity singularity. Here's my current broken implementation:
Layout::Layout(QQuickItem * parent) : QQuickItem(parent) {}
LayoutList & Layout::model() const
{
return m_model;
}
void Layout::setModel(const LayoutList & model)
{
if (m_model == model)
return;
m_model = model;
emit modelChanged();
}
So, how can I make it so that I can use this extended qml class with my LayoutModel?
QObjects do not have a copy constructor, so you must use a pointer:
*.h
class Layout : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(LayoutModel *model READ model WRITE setModel NOTIFY modelChanged)
private:
LayoutModel *m_model;
public:
explicit Layout(QQuickItem * parent = nullptr);
LayoutModel *model();
void setModel(LayoutModel * model);
...
signals:
void modelChanged();
};
*.cpp
...
LayoutModel *Layout::model()
{
return m_model;
}
void Layout::setModel(LayoutModel *model)
{
if (m_model == model)
return;
m_model = model;
emit modelChanged();
}

Why I get an "use of delete function" from a class that inherits from QAbstractListModel?

I am trying to implement a ListView. So far, I have implemented a class named PacientModel that inherits of QAbstractListModel.
#ifndef PACIENTMODEL_H
#define PACIENTMODEL_H
#include <QAbstractListModel>
class PacientModel : public QAbstractListModel {
Q_OBJECT
public:
enum PacientRole {
CIRole,
NameRole,
LastNameRole
};
Q_ENUM(PacientRole)
PacientModel(QObject * parent = nullptr);
int rowCount(const QModelIndex & = QModelIndex()) const;
QVariant data (const QModelIndex &index, int role = Qt::DisplayRole) const;
QHash<int, QByteArray> roleNames() const;
Q_INVOKABLE QVariantMap get(int row) const;
Q_INVOKABLE void append (const QString &CI, const QString &name, const QString &lastName);
Q_INVOKABLE void set (int row, const QString & CI, const QString &name, const QString &lastName);
Q_INVOKABLE void remove (int row);
private:
struct Pacient {
QString CI;
QString name;
QString lastName;
};
QList<Pacient> m_pacients; };
#endif // PACIENTMODEL_H
I also have the implementation of the ListView, but when I compiled the code, I got this error.
C:\Qt\Qt5.8.0\5.8\android_armv7\include\QtCore\qmetatype.h:765: error: use of deleted function 'PacientModel::PacientModel(const
PacientModel&)'
return new (where) T(*static_cast(t));
How can I resolve this?
Per this Q&A QAbstractListModel's copy constructor is marked as private. That means that it is not copyable and therefore, as you inherited from it, your derived class is also not copyable by default.
If you want to make copies of your class then you need to manually define a copy constructor for the class instead of relying on the compiler doing it for you (since it won't).
After several days I discovered that the error wasn't in the class declaration, It was in the way I assigned the data to the listview... I was using this:
objetoLP->setProperty("modelList", QVariant::fromValue(pacientes));
instead of:
ctxt->setContextProperty("modelList", &pacientes);

How to get the old value when handling QAbstractItemModel::dataChanged() signal?

I have a QTableView which have set a QStandardItemModel. The user edits data in some index in the view and then the model emits the dataChanged() signal. In the SLOT where I am handling the SIGNAL I have the QModelIndex range of the user changes and thus I can get the new values the user have entered. How can I obtain the old values at that point?
After some research I figured out that there is no standard way to achive this behaviour. To solve the problem I had to inherit QStandardItemModel and reimplement setData() like this:
class RecallModel : public QStandardItemModel
{
public:
RecallModel (QObject * parent = 0) : QStandardItemModel(parent) {}
// Reimplemented
bool setData(const QModelIndex &index, const QVariant &value, int role= Qt::EditRole)
{
// backup the previous model data
if (role == Qt::EditRole || role == Qt::DisplayRole)
QStandardItemModel::setData(index, data(index), Qt::UserRole + 1);
return QStandardItemModel::setData(index, value, role);
}
};
And after that I can access the old data in the slot handling the dataChanged() signal:
void SomeObject::handleDataChange(const QModelIndex &topLeft, const QModelIndex &bottomRight)
{
...
const QVariant &vOldData = index.data(Qt::UserRole + 1); // here is the old data
const QVariant &vNewData = index.data(Qt::DisplayRole); // here is the new data
...
}
Since QStandardItemModel is a simple model, it does not have a signal with this feature. If you want such a feature you can subclass QAbstractItemModel and have your own custom class and implement setData and emit a custom signal which contains both the old and the new values.
As a workaround you can connect the itemChanged signal of QStandardItemModel to some slot :
connect(model,SIGNAL(itemChanged(QStandardItem*)),this, SLOT(onModelChanged(QStandardItem*)));
And store the new value as a Qt::UserRole in model to use it as the old value when the slot is called next time :
void MyClass::onModelChanged(QStandardItem *item)
{
disconnect(model,SIGNAL(itemChanged(QStandardItem*)),this, SLOT(onModelChanged(QStandardItem*)));
QVariant oldValue = item->data(Qt::UserRole);
item->setData(item->data(Qt::DisplayRole), Qt::UserRole); //Store the new value for next use
connect(model,SIGNAL(itemChanged(QStandardItem*)),this, SLOT(onModelChanged(QStandardItem*)));
}
User can change data with delegate so possible solution is:
#ifndef ITEMDELEGATE_H
#define ITEMDELEGATE_H
#include <QItemDelegate>
class ItemDelegate : public QItemDelegate
{
Q_OBJECT
public:
explicit ItemDelegate(QObject *parent = 0);
protected:
QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const;
void setEditorData(QWidget * editor, const QModelIndex & index) const;
void setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const;
void updateEditorGeometry(QWidget * editor, const QStyleOptionViewItem & option, const QModelIndex & index) const;
signals:
void dataChanged(QString oldValue,QString newValue) const;
public slots:
private:
mutable QString old;//we want change member data in const method
};
#endif // ITEMDELEGATE_H
As you can see many methods are const by default so I did some tricks (such as mutable) to avoid problems. Also in my edited answer I doesn't store old data in UserRole+1 , all done with old mutable variable.
cpp:
#include "itemdelegate.h"
#include <QLineEdit>
#include <QDebug>
ItemDelegate::ItemDelegate(QObject *parent) :
QItemDelegate(parent)
{
}
QWidget *ItemDelegate::createEditor(QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
QLineEdit *editor = new QLineEdit(parent);
return editor;
}
void ItemDelegate::setEditorData(QWidget *editor,
const QModelIndex &index) const
{
old = index.model()->data(index, Qt::EditRole).toString();//store old data
QLineEdit *line = qobject_cast<QLineEdit*>(editor);
line->setText(old);
}
void ItemDelegate::setModelData(QWidget *editor,
QAbstractItemModel *model,
const QModelIndex &index)const
{
QLineEdit *line = static_cast<QLineEdit*>(editor);
QString data = line->text();
emit dataChanged(old, line->text());
model->setData(index, data);
}
void ItemDelegate::updateEditorGeometry(QWidget *editor,
const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
editor->setGeometry(option.rect);
}
Usage:
ItemDelegate * del = new ItemDelegate;
connect(del,&ItemDelegate::dataChanged,[=](QString oldValue,QString newValue) {
qDebug() << "old" << oldValue<< "new" <<newValue ;
});
ui->tableView->setItemDelegate(del);
I tested it and it works. It will work with different models. QTableView uses lineEdit as delegate by default, so user will not see any changes in view.
I used here C++11 (CONFIG += c++11 to .pro file) and new syntax of signals and slots, but of course you can use old syntax if you want.
All solutions before this answer rely on Qt-specific features. But you can move INSERT, UPDATE, DELETE (not SQL, but common) functionality outside Qt framework. Look at Unit Of Work Design Pattern, and related Domain Object pattern from "Patterns of Enterprise Application Architecture".
You can modify this patterns to hold old values and get it when you modify during setData().

Qt - reduce duplication between two QAbstractListModel subclasses

I've written two QAbstractListModel subclasses:
class Model1: public QAbstractListModel {
Q_OBJECT
public:
int rowCount(const QModelIndex& parent=QModelIndex()) const;
QVariant data(const QModelIndex& index, int role) const;
void clear();
private:
QVector<Obj1*> m_items;
};
and
class Model2: public QAbstractListModel {
Q_OBJECT
public:
int rowCount(const QModelIndex& parent=QModelIndex()) const;
QVariant data(const QModelIndex& index, int role) const;
void clear();
private:
QVector<Obj2*> m_items;
};
In addition, these two classes have a slot AddObj() which is dependent on the pointer used (Obj1* or Obj2*).
The matter is, constructors and destructors are the same, and so are rowCount and clear (they do the exact same thing). The only differences are in data and in what m_items is.
What's the best strategy to reduce duplication here? I tried with a base class:
class ModelBase: public QAbstractListModel {
Q_OBJECT
ModelBase(QObject* parent=0);
~ModelBase();
public:
virtual int rowCount(const QModelIndex& parent=QModelIndex()) const;
virtual QVariant data(const QModelIndex& index, int role) const = 0;
virtual void clear();
private:
QVector<QString*> m_items; // dummy vector
};
and then have the other classes derive from it:
class ModelDerived1: public ModelBase {
Q_OBJECT
public:
ModelDerived1(QObject* parent=0);
~ModelDerived1();
QVariant data(const QModelIndex& index, int role) const = 0;
private:
QVector<Obj1*> m_items;
};
The problem is that in such a case the derived class's data function is never called when this model is attached to a view.
Nevertheless, the derived class data() is not called (I put in debug statements, and they are never executed).
What am I doing wrong here?
EDIT: The solution is not what I posted here. In fact, it was completely off. The issue is in the rowCount function and in the private m_items QVector.
rowCount, if not present, calls the superclass method, which does not use the private m_items element from the derived class, but it uses the one from the superclass, which being dummy, returns always a size of 0, and thus data never gets called.

Getting multiple errors creating a model in Qt

I'm creating a class in Qt. But everything was working until I built a tablemodel class. I'm now getting the errors "expected ( before * token" and "Creator does not name a type".
What's the problem? It seems very cryptic.
#ifndef OPENMODEL_H
#define OPENMODEL_H
#include <QAbstractTableModel>
#include <QString>
#include <QObject>
#include "creator.h"
namespace language
{
class OpenModel : public QAbstractTableModel
{
Q_OBJECT
public:
explicit OpenModel(Creator* creator, QObject *parent = 0); // Creater* throws a expected ) before * token
// QAbstractTableModel Model view functions
int rowCount(const QModelIndex &parent = QModelIndex()) const ;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
// QAbstractTableModel Model edit functions
bool setData(const QModelIndex & index, const QVariant & value, int role);
Qt::ItemFlags flags(const QModelIndex &index) const;
// Functions to manipulate creator
void add(QString name, QString file);
void remove(int index);
// Functions to move files up and down
void moveup(int index);
void movedown(int index);
private:
Creator* creator; // Creator does not name a type
};
}
#endif // OPENMODEL_H
this is creator.h
/*
This is the main file for the language-creator
It controls the addition, deletion and change of the centances (files)
It shall be passed by pointer to the models to be proccessed
*/
#ifndef CREATOR_H
#define CREATOR_H
#include <QObject>
#include <QVector>
#include "file.h"
#include "openmodel.h"
#include "setmodel.h"
namespace language
{
class Creator
{
public:
Creator();
void addFile(const File& f); // Adds a file to the vector
bool removeFile(int index); // Remove a file from the vector
bool replaceFile(int index, const File& f); // Replaces a file at index
const QVector<File>* getFiles() const; // Returns a list of the files
OpenModel getOpenModel() const; // Returns a pointer to the open model
SetModel getSetModel() const; // Returns a pointer to the set model
void reset(); // This resets the class to an initialized state
private:
QVector<File> files; // This holds all the files
};
}
#endif // CREATOR_H
You have a cyclic reference between these header-files. openmodel.h includes creator.h and the other way around. So, when creator.cpp (I presume there is such a file) gets compiled, it will include openmodel.h before the class Creator is declared (remember that #include means that the contents of the file will be pasted right there), hence you get the error.
To avoid this, you could remove the #include "creator.h" from openmodel.h, and instead add a forward declaration:
class Creator;
Put the declaration right before the class OpenModel. Since you only use pointers to Creator in that class, this will work fine.
Your creator.h file includes openmodel.h which uses the Creator identifier before createor.h has had a change to delclare it.
Put a class Creator; forward declaration in openmodel.h.