Changing a Qt AbstractListModel without using in-place editing - c++

I currently have a QtAbstractListModel subcliass (shortened for clarity):
class HolidayTask;
class HolidayTaskModel: public QAbstractListModel
{
Q_OBJECT
public:
explicit HolidayTaskModel(QObject *parent = 0);
~HolidayTaskModel();
int rowCount(const QModelIndex& parent = QModelIndex()) const;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
Qt::ItemFlags flags(const QModelIndex& index) const;
bool insertRows(int row, int count, const QModelIndex& parent = QModelIndex());
bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex());
bool setData(const QModelIndex& index, const QVariant& value,
int role = Qt::EditRole);
private:
QVector<HolidayTask*> m_items;
};
Where HolidayTask is the items I'm containing. I'm trying to figure out what needs to be reimplemented for this use case:
The model is modifiable, but not with in place editing in the view: another widget (not a *View subclass) will do the editing, outside from the view this model will be shown in;
It needs to implement not only appending (that would be easy) but also inserting and reordering.
In the case of appending, it would be very easy to make an appendTask function that calls beginInsertRows and endInsertRows, however at least inserting and/or removing is not as trivial.
Most of the code examples I've found around deal with in-place editing (createEditor, etc.), which, as I've wrote above, is not what I'm needing. What should I be implementing to modify this model to accomplish this task? Alternatively, are there any code examples that show this modus operandi?

If you don't need to edit your model data using views, you don't need to implement insertRows, removeRows and setData. Instead, you should create your own modifying functions, e.g. add_task(HolidayTask* task), set_task(int row, HolidayTask* task) and remove_task(int row). In these functions you need to change m_items value to reflect data changes. (Besides, your should switch from QVector to QList if you need insertions and deletions from the middle of the list to be fast). Additionally, you should notify views about the changes:
Call beginRemoveRows before removing a row and endRemoveRows after that.
Call beginInsertRows before inserting a row and endInsertRows after that.
Call emit dataChanged(...) after changing data.

Inserting is like appending, just with a row != m_items.size():
void HolidayTaskModel::addTask(HolidayTask* task)
{
const int row = ...find position...
beginInsertRows(QModelIndex(), row, row);
m_items.insert(row, task);
endInsertRows();
}
Remove a task:
void HolidayTaskModel::removeTask(HolidayTask* task)
{
const int row = m_items.indexOf(task);
if (row == -1)
return;
beginRemoveRows(QModelIndex(), row, row);
delete m_items[row]; //only if the item is owned by the model
m_items.remove(row);
endRemoveRows();
}
For reordering, it depends on your exact use case, for moving single items use beginMoveRows()/endMoveRows(), for sorting reimplement sort() or, often easier, leave the items in the base model unsorted and let a QSortFilterProxyModel proxy do the sorting.

Related

how I use the listView from QT to take and show my vector?

have two functions .getProjects() and .getEmployees(), from another class "manage.h", that basically return a vector with all the projects/employees I've added until now.
How can I show them in my QT interface?
I've created two buttons (in a stackedWidget), one to get all the projects when clicked and the other one for the employees, and I've also used a listView that should show all my data when I click one of the two buttons, but how I tell the listView to take and show my function?
void MainWindow::on_showAllProjects_pushButton_clicked()
{
ui->listView->something(manage.getProjects());
}
You first need a model for your QListView.
QListView inherits QAbstractItemView which has the method of adding a model.
QItemSelectionModel *m = ui->listview->selectionModel();
ui->listview->setModel(new model);
Then you can start inserting rows into this model, by using a method from QAbstractItemModel, which is this one:
bool insertRow(int row, const QModelIndex &parent = QModelIndex())
Then use this method to set the data of this last item you inserted (the last row), you can access it by providing the last index of the model
virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole)
Set the first argument to ui->listview->model->rowCount()
And the second element is an element from your vector, so you gonna have to iterate through it.

How can I selectively indent the content of a row in a QTableView?

I am relatively new to QT Creator.
I have a specific need that requires me to indent the text inside a cell of some specific rows in a QTableView, according to a certain parameter. I am using a custom model subclassed from the QAbstractTableModel.
Below is my minimal example:
I have a Class named Variable which is the object that needs to be displayed on the QTableView. We will use 2 columns : 1 for the Name of variable and 1 for its Value. We only want to indent the Name of certain variables.
Each Variable object has a parameter called IndentParameter which will be our criterion.There are only 3 values for IndentParameter : 0 , 1 and 2.
If the IndentParameter is 0, nothing needs to be done. IndentParameter= 1 means we indent 1 time and paint the cell, and IndentParameter= 2 means we indent twice and paint the cell with a different color.
class Variable
{
public:
Variable();
~Variable();
QString getName() const;
void setName(const QString newName);
int getIndentParamater() const;
void setIndentParamater(const int newInt);
int getValue() const;
void setValue(const int newValue);
private:
QString m_name;
int m_IndentParamater;
int m_value;
};
The desired output could look something like this:
Desired indentation on Variable Names
At the moment the color differenciation is realized inside the model: (incomplete but just for clarification)
VariableModel::data(const QModelIndex &index, int role) const
if (role == Qt::BackGroundRole)
{
if (VariableList.at(index.row())->getIndentParameter) == 1)
return QBrush(Qt::cyan)
if (VariableList.at(index.row())->getIndentParamater) == 2)
return QBrush(Qt::green)
}
I have already tried adding " " (blank spaces) to the name of the variables but I need their name to be intact for some other features to work properly.
Since I am using the Model View Architecture, I figured there could be a clever way of doing this via subclassing a Delegate but I have to admit I have never done this before and don't know much about them.
Thank you very much in advance for taking the time to read.
You can try something like:
class MyDelegate: public QStyledItemDelegate{
public:
using QStyledItemDelegate::QStyledItemDelegate;
void initStyleOption(QStyleOptionViewItem *option, const QModelIndex &index) const override{
QStyledItemDelegate::initStyleOption(option, index);
int indent = index->data(IndentRole).toInt();
option->text.prepend(QString(' ', indent));
}
};
then set this delegate to your view.
Here you add space only in data to be painted and this won't affect your model's data.
Another option is to reimplement the QStyleItemDelegate::paint, where you can directly draw your text. But here the example above is much more simple.

sourceModel()->createIndex() in QAbstractProxyModel sub-class

I am attempting to create a proxy model that dynamically maps items from source model.
Following the implementation of QIdentityProxyModel with the intention to go from there I have discovered that it is in fact impossible to replicate it by examining the 4 core functions:
mapFromSource()
mapToSource()
index()
parent()
Consider this based on QIdentityProxyModel:
mapFromSource()
QModelIndex ProxyModel::mapFromSource(const QModelIndex &sourceIndex) const
{
if(sourceIndex.isValid())
return createIndex(sourceIndex.row(), sourceIndex.column(), sourceIndex.internalPointer());
else
return QModelIndex();
}
mapToSource()
QModelIndex ProxyModel::mapToSource(const QModelIndex &proxyIndex) const
{
if(proxyIndex.isValid())
return sourceModel()->createIndex(proxyIndex.row(), proxyIndex.column(), proxyIndex.internalPointer());
else
return QModelIndex();
}
index()
QModelIndex ProxyModel::index(int row, int column, const QModelIndex &parent) const
{
const QModelIndex sourceParent = mapToSource(parent);
const QModelIndex sourceIndex = sourceModel()->index(row, column, sourceParent);
return mapFromSource(sourceIndex);
}
parent()
QModelIndex ProxyModel::parent(const QModelIndex &index) const
{
const QModelIndex sourceIndex = mapToSource(index);
const QModelIndex sourceParent = sourceIndex.parent();
return mapFromSource(sourceParent);
}
THE ISSUE
Problem lies in mapToSource() line
return sourceModel()->createIndex(proxyIndex.row(), proxyIndex.column(), proxyIndex.internalPointer());
The QAbstractItemModel::createIndex is protected function and cannot be used unless the caller is declared friend. That is obviously not an option without modifying the QAbstractItemModel class directly. The only alternative is to use regular QAbstractItemModel::index but that requires parent in form of QModelIndex as one of the arguments. However doing this:
return sourceModel()->index(proxyIndex.row(), proxyIndex.column(), proxyIndex.parent());
causes an infinite loop due to the fact that parent() function relies on mapToSource() and vice versa now.
The alternative approach shown for example here relies on the stored map of QPersistentModelIndex objects that are queried in the above mentioned functions. This approach while viable has several disadvantages:
It requires a lots of extra memory to store the indexes
All persistent indexes need to be updated by the source model upon every structure change (as opposed to on demand index lookup with dynamic models)
Queries to the map may be slow when iterating over kyes of the map (this can be remedied by making two identical maps in reverse at the expense of yet more memory)
Hence my questions:
Is there another way to handle the hierarchical proxy model dynamically without relying on createIndex() and without running into infinite loop of function calls?
Is it in fact necessary to create and maintain the structure of the proxy model in a storage vis-à-vis the source structure or is there a way to create a dynamic proxy model?
Thanks!
Perhaps this is a late reply, but I've just faced the same problem. I solved it by casting sourceModel() to my model, meanwhile I declared my ProxyModel as a friend.
This is my mapToSource definition:
QModelIndex ProxyModel::mapToSource(const QModelIndex& proxyIndex) const
{
Model* pModel = qobject_cast<Model*>(sourceModel());
if (!pModel || !proxyIndex.isValid()) return QModelIndex();
...
return pModel->createIndex(row, col, proxyIndex.internalPointer());
}
and the friend declaration:
class Model : public QAbstractTableModel
{
Q_OBJECT
friend class ProxyModel;
...
};
I understand your concern about using only QAbstractItemModel interface (believe me, I had the same), but let's face it the ProxyModel can only be used with my Model and nothing else (at least according to my implementation). Therefore I don't see anything bad in "friendship" like this. As for UB there shouldn't be any issues here as well, because qobject_cast would return 0 if other model other than mine was provided.
Building on the previous answers, I would like to add some alternate ways
First, the uglyest hack in the history of time itself is safe-ish on most compile:
class HackyDummy : public QAbstractItemModel
{
friend class ProxyModel;
};
QModelIndex ProxyModel::createSourceIndex(int r, int c, void *i) {
return ((HackyDummy*) sourceModel())->createIndex(r,c,i);
}
Now, of course, this is a terrible, terrible hack. It also wont compile in some compiler as the C++ spec isn't very clear (or at least as been mis-interpreted by some) on accessing parent class private and protected methods from sub-class friends. This lead us to a better plan. But first, a little detail: QIdentityProxyModel does it (almost). QIdentityModel own mapToSource/mapFromSource are public and re-implementable, so is its createIndex. So, it is possible do an internal/private QIdentityProxyModel with a method to create our little source indices from the row, column and internal pointers by accessing the hack contained inside of QIdentityProxyModel itself.
Edit: typo
I just inherited from QIdentityProxyModel and used it's mapToSource method in my mapToSourceMethod.
QModelIndex PropertyModel::mapToSource(const QModelIndex & proxyIndex) const
{
if(hasNoModel())
{
return QModelIndex();
}
QModelIndex remapped = createIndex( /* transform proxyIndex here */ );
return QIdentityProxyModel::mapToSource(remapped);
}
It means you don't end up with a cast that could crash your code should you ever change the source model type and forget to update your cast.

Displaying std::map in a QTableView

I have a some sort of data stored in a std::map and need to display that in a QTableView.
So I've my model class from QAbstractItemModel, but I'm faced with a problem:
The "data" method gives me a QModelIndex that contains the expected row-number of the data-entry. But since I'm using a map and not a vector, I can't randomly access it with the row parameter.
So my idea was to overwrite the "index"-method that generates the QModelIndex object and contain the appropriate hash-key for each object.
But that doesn't make it any easier, since I'd be required to have to get the hash-key by row-number again.
Of course i could iterate through the entire map from begin() to end() to find the n-th row/element, but that would be extremely inefficient.
Do you have any advice how to approprietly display std::map in a QTableView?
struct Data {
...
};
std::map<int, Data> dataMapping;
QModelIndex index ( int row, int column, const QModelIndex & parent );
QVariant data ( const QModelIndex & index, int role );

Qt custom tree model of nested vector

I've got a nested data structure I'd like to display with a QTreeView.
Let's say I've got something like this:
class Image
{
public:
...
std::vector<Filter> filter_;
};
typedef std::vector<Image> Gallery;
typedef std::vector<Gallery> Galleries;
The QTreeView should display the MultiGallery like this:
Gallery1
|_____Image1
|_____Image2
|_____Image3
Gallery2
|_____Image1
| |_____Filter1
| |_____Filter2
|_____Image2
I read the Qt Model View examples, i know I have to derive from QAbstractItemModel to create a treemodel and implement the member functions:
QVariant data(const QModelIndex &index, int role) const;
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const;
QModelIndex parent(const QModelIndex &index) const;
int columnCount(const QModelIndex &parent=QModelIndex()) const;
int rowCount(const QModelIndex &parent=QModelIndex()) const;
I just don't know whats the best way to implement these, especially the index function.
The main idea is that having an Index (that is row, column and internalId or internalPointer) you should be able to identify both item and its parent.
You data structure does not fit this requirement. You should add links to parent objects to your objects, or use some auxiliary structure to store this information.
Then, you can store pointers to your items (or pointer to auxiliary structure, or better index of auxiliary in array of structures) in indices.