I have a main data model and 2 tables, each of which uses a proxy model. In this model, there is one important point m_check_box_states. I put a checkbox in the 0th column, by clicking on which I essentially select the entire row
#include <QAbstractTableModel>
class EditableTableModel : public QAbstractTableModel {
public:
EditableTableModel(QObject *parent = nullptr);
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;
bool setData(const QModelIndex &index, const QVariant &value,
int role = Qt::EditRole) override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
void addEntry(const std::vector<QString> &newEntry);
void beginInsertion(int size);
void endInsertion();
QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const override;
void setHeaders(const QStringList& headers);
private:
std::vector<std::vector<QString>> m_data;
std::vector<bool> m_check_box_states;
QStringList m_headers;
};
EditableTableModel::EditableTableModel(QObject *parent) : QAbstractTableModel(parent),
m_check_box_states(std::vector<bool>(rowCount(), false))
{
}
int EditableTableModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return m_data.size();
}
QVariant EditableTableModel::data(const QModelIndex &index, int role) const
{
const bool isInvalidIndex = !index.isValid() ||
index.row() >= rowCount() ||
index.column() >= columnCount();
if (isInvalidIndex)
return QVariant();
if (role == Qt::CheckStateRole && index.column() == 0)
{
return m_check_box_states.at(index.row()) ? Qt::Checked : Qt::Unchecked;
}
if (role == Qt::DisplayRole)
{
return m_data.at(index.row()).at(index.column());
}
return QVariant();
}
bool EditableTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
const bool isInvalidIndex = !index.isValid() ||
index.row() >= rowCount() ||
index.column() >= columnCount();
if (isInvalidIndex)
return false;
if (role == Qt::EditRole)
{
m_data.at(index.row()).at(index.column()) = value.toString();
emit dataChanged(index, index, {role});
return true;
}
else if (role == Qt::CheckStateRole && index.column() == 0)
{
m_check_box_states.at(index.row()) = value.toBool();
emit dataChanged(index, index, {role});
return true;
}
return false;
}
Qt::ItemFlags EditableTableModel::flags(const QModelIndex &index) const
{
Qt::ItemFlags flags = QAbstractTableModel::flags(index);
if (!index.isValid())
return Qt::NoItemFlags;
if (index.column() == 0) {
flags |= Qt::ItemIsUserCheckable | Qt::ItemIsEditable;
} else {
flags |= Qt::ItemIsEditable;
}
return flags;
}
void EditableTableModel::addEntry(const std::vector<QString> &newEntry)
{
Q_ASSERT(newEntry.size() == (size_t)columnCount());
m_check_box_states.emplace_back(false);
m_data.emplace_back(newEntry);
}
int EditableTableModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return m_headers.size();
}
void EditableTableModel::beginInsertion(int size)
{
int start_index = m_data.size();
int new_size = start_index + size;
int end_index = new_size - 1;
beginInsertRows(QModelIndex(), start_index, end_index);
m_data.reserve(m_data.size() + size);
m_check_box_states.reserve(m_data.size() + size);
}
void EditableTableModel::endInsertion()
{
endInsertRows();
}
void EditableTableModel::setHeaders(const QStringList &headers)
{
m_headers = headers;
emit headerDataChanged(Qt::Horizontal, 0, m_headers.size() - 1);
}
QVariant EditableTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
return m_headers.at(section);
}
return QVariant();
}
My 2 proxy models.
The first ColumnFilterProxyModel displays specific columns that I specify as an array. My model has 8 columns and I display 3 columns in one table and the remaining 5 in the other, but not at once!
#include <QSortFilterProxyModel>
#pragma once
class ColumnFilterProxyModel : public QSortFilterProxyModel {
public:
ColumnFilterProxyModel(const QList<int> &columns, QObject *parent = nullptr);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role) const override;
bool setData(const QModelIndex &index, const QVariant &value,
int role = Qt::EditRole) override;
protected:
QModelIndex mapToSource(const QModelIndex &proxyIndex) const override;
QModelIndex mapFromSource(const QModelIndex &sourceIndex) const override;
private:
QList<int> m_columns_nums;
};
#include "columnfilterproxymodel.h"
#include <iostream>
ColumnFilterProxyModel::ColumnFilterProxyModel(const QList<int> &columns,
QObject *parent) :
QSortFilterProxyModel(parent),
m_columns_nums(columns) {}
int ColumnFilterProxyModel::rowCount(const QModelIndex &parent) const
{
return parent.isValid() ? 0 : sourceModel()->rowCount();
}
int ColumnFilterProxyModel::columnCount(const QModelIndex &parent) const
{
return parent.isValid() ? 0 : m_columns_nums.size();
}
QVariant ColumnFilterProxyModel::data(const QModelIndex &index, int role) const
{
const bool isInvalidIndex = !index.isValid() ||
index.row() >= sourceModel()->rowCount() ||
index.column() >= columnCount();
if (isInvalidIndex || (role != Qt::DisplayRole &&
role != Qt::EditRole &&
role != Qt::CheckStateRole))
return QVariant();
QModelIndex sourceIndex = this->mapToSource(createIndex(index.row(), index.column()));
return sourceModel()->data(sourceIndex, role);
}
bool ColumnFilterProxyModel::setData(const QModelIndex &index,
const QVariant &value,
int role)
{
QModelIndex sourceIndex = mapToSource(this->index(index.row(), m_columns_nums.at(index.column())));
return sourceModel()->setData(sourceIndex, value, role);
}
QModelIndex ColumnFilterProxyModel::mapToSource(const QModelIndex &proxyIndex) const
{
const bool isInvalidIndex = !proxyIndex.isValid() ||
proxyIndex.row() >= sourceModel()->rowCount() ||
proxyIndex.column() >= columnCount();
if(isInvalidIndex)
return QModelIndex();
return sourceModel()->index(proxyIndex.row(), m_columns_nums.at(proxyIndex.column()));
}
QModelIndex ColumnFilterProxyModel::mapFromSource(const QModelIndex &sourceIndex) const
{
const bool isInvalidIndex = !sourceIndex.isValid() ||
sourceIndex.row() >= rowCount() ||
sourceIndex.column() >= columnCount();
if (isInvalidIndex)
return QModelIndex();
int proxyColumn = m_columns_nums.at(sourceIndex.column());
return createIndex(sourceIndex.row(), proxyColumn);
}
#include "columnfilterproxymodel.h"
class CheckableFilterProxyModel : public ColumnFilterProxyModel {
public:
CheckableFilterProxyModel(const QList<int> &columns,
QObject *parent = nullptr);
QVariant data(const QModelIndex &index, int role) const override;
protected:
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
QModelIndex mapToSource(const QModelIndex &proxyIndex) const override;
QModelIndex mapFromSource(const QModelIndex &sourceIndex) const override;
};
In this proxy model I want to display the 5 remaining columns, but only those rows that were selected using the checkbox. The 5 columns are displayed correctly, but I can’t really figure out how to filter the rows. The problem is most likely here and most likely with the indexes.
#include "checkablefilterproxymodel.h"
#include <iostream>
bool CheckableFilterProxyModel::filterAcceptsRow(int sourceRow,
const QModelIndex &sourceParent) const
{
QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
return ColumnFilterProxyModel::data(index, Qt::CheckStateRole).toBool();
}
QModelIndex CheckableFilterProxyModel::mapToSource(const QModelIndex &proxyIndex) const
{
return ColumnFilterProxyModel::mapToSource(proxyIndex);
}
QModelIndex CheckableFilterProxyModel::mapFromSource(const QModelIndex &proxyIndex) const
{
std::cout << proxyIndex.row() << " " << proxyIndex.column() << std::endl;
return QModelIndex();
}
QVariant CheckableFilterProxyModel::data(const QModelIndex &index, int role) const
{
return ColumnFilterProxyModel::data(index, role);
}
CheckableFilterProxyModel::CheckableFilterProxyModel(const QList<int> &columns,
QObject *parent) : ColumnFilterProxyModel(columns, parent)
{
}
I need to make it so that the rows selected by the checkbox in one table are displayed in another.
This is how I initialize the columns and the QTableView where it all is displayed
main_model = new EditableTableModel();
main_model->setHeaders({
"Model Name",
"Color1",
"Color2",
"Picture",
"Line Style",
"Fill Style",
"Model Name2",
"Model Name3"});
QList<int> columns1 = {0, 1, 2, 3, 4, 5};
CheckableFilterProxyModel *proxyModel1 = new CheckableFilterProxyModel(columns1);
proxyModel1->setSourceModel(main_model);
editor_tableView->setModel(proxyModel1);
editor_tableView->setShowGrid(false);
QList<int> columns3 = {0, 6, 7};
ColumnFilterProxyModel *proxyModel3 = new ColumnFilterProxyModel(columns3);
proxyModel3->setSourceModel(main_model);
proxyModel3_tableView->setModel(proxyModel3);
Here is an example of the data I have in the model:
{"instance", "red", "red", "none", "none", "solid", "instance", "label"}
{"instance", "blue", "blue", "none", "none", "solid", "instance", "drawing"}
{"_gdsii__", "green", "none", "none", "none", "solid", "instance3", "instance3" }
Accordingly, I have 3 rows. Having selected the 3rd row in one table, in the second I see the row under index 0, although I expect to see the row under index 3, all attention is on name
Here is the selection in one table
This is what is added to the other one
Actually, there is an error in the indexes and it is probably here
QModelIndex CheckableFilterProxyModel::mapToSource(const QModelIndex &proxyIndex) const
{
return ColumnFilterProxyModel::mapToSource(proxyIndex);
}
But it is not clear to me how I can recalculate the index for all selected elements at this stage and display them correctly.
P.S:
What I tried was saving the sourceRow in the QSet in the filterAcceptsRow function, but this approach seems hacky to me, because filterAcceptsRow is a constant method. Also, adding a getter to get std::vector m_check_box_states and already having this vector in my proxy model to calculate the index seems to be also not a very pleasant solution, because for each selection I will have to run through all the elements of this vector, and I will have to take into account a lot of things. Actually, it seems to me that I do not understand well how all these proxy models work in QT, and that is why my solutions, in my own opinion, are idiotic