Displaying std::map in a QTableView - c++

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 );

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.

parent parameter for qmodelindex when getting the index

Here is the line of code:
QModelIndex id = tm->index(r, ec, QModelIndex());
So i have my own model which is tm and i am trying to access its index. r stands for the row and ec is for the column. I notice from examples that they add QModelIndex() on the third parameter. The third parameter stands for the parent index.
tm is just a simple QTableView. Is it really necessary to provide QModelIndex ? I tried reading the Qt manual but i can't seem to find any simple explanations when to supply a parent index.
Quoting from the documentation:
An invalid model index can be constructed with the QModelIndex constructor. Invalid indexes are often used as parent indexes when referring to top-level items in a model.
...
Each top-level item in a model is represented by a model index that does not have a parent index - in this case, parent() will return an invalid model index, equivalent to an index constructed with the zero argument form of the QModelIndex() constructor.
So if you do not have nested data (like in your QTableView), the parent index will always be an invalid one.
The signature of QAbstractItemModel::index is:
QModelIndex QAbstractItemModel::index(int row, int column, const QModelIndex & parent = QModelIndex()) const
The last parameter is optional, in your case you can just omit it to supply an invalid model index.

Changing a Qt AbstractListModel without using in-place editing

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.

QSortFilterProxyModel sort multiple columns

I am trying to implement a table that is sortable on more than one column. Qt's QSortFilterProxyModel only supports sorting on one column (at least in Qt 4.6.2).
I've found this solution by dimkanovikov on github, but it lacks dynamic updating on added rows. What I mean by this, is that the model is changed and the beginInsertRows(), beginRemoveRows(), their corresponding end..-methods and the dataChanged() signals are emitted. Ideally I would like to only these rows to be updated, but the model should at least react to such changes.
There's another FAQ item on Qt's site that sorts a QTableWidget, but it lacks dynamic updating, too.
I am new to Qt and I'd like to get some pointers on how I should go about this.
You can set the sorting role of the QSortFilterProxyModel to something different then the default Qt::DisplayRole withsetSortRole(Qt::UserRole). Then, in your model's data() method return a proper sort key if it gets called with the role Qt::UserRole, e.g. by concatenating the strings of the involved columns.
There's one slightly inelegant solution, that is always used to sort multiple columns.
You have to subclass QSortFilterProxyModel and reimplement bool lessThan(const QModelIndex &rLeft, const QModelIndex &rRight) const. Instead of only comparing between the two given indices, check all the columns:
int const left_row = rLeft.row();
int const right_row = rRight.row();
int const num_columns = sourceModel()->columnCount();
for(int compared_column = rLeft.column(); compared_column<num_columns; ++compared_column) {
QModelIndex const left_idx = sourceModel()->index(left_row, compared_column, QModelIndex());
QModelIndex const right_idx = sourceModel()->index(right_row, compared_column, QModelIndex());
QString const leftData = sourceModel()->data(left_idx).toString();
QString const rightData = sourceModel()->data(right_idx).toString();
int const compare = QString::localeAwareCompare(leftData, rightData);
if(compare!=0) {
return compare<0;
}
}
return false;
Then you can call sort(0) on your QSortFilterProxyModel subclass and it will sort all the columns. Also don't forget to call setDynamicSortFilter(true) when you want the sorted rows to be dynamically resorted when the model data changes.
To support sorting on arbitrary columns in ascending or descending order, you would have to keep this info in a QList and compare accordingly when lessThan is called. In the list you would have the columns in order of their priority and do the comparisons in the same order. You should also sort the other "inactive" columns in some predefined order, otherwise they will not be sorted by default.

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.