I'm attempting to create a QTreeView and use a custom model for it. I have placed qDebug() statements at various places, and I have determined that data() is never being called. How can I fix this problem?
The model's code is below
#include "ModelItemNeural.h"
ModelItemNeural::ModelItemNeural(QObject *parent, NeuralNode *rootNode)
: QAbstractItemModel(parent)
{
this->rootNode = 0;
}
QModelIndex ModelItemNeural::index(int row, int column, const QModelIndex &parent) const
{
// Out of bounds and null rootNode check.
if (rootNode == 0 || row < 0 || column < 0)
{
return QModelIndex();
}
NeuralNode* parentNode = nodeFromIndex(parent);
NeuralNode* childNode = parentNode->getInputs().value(row);
if (childNode == 0)
{
return QModelIndex();
}
return createIndex(row, column, childNode);
}
QModelIndex ModelItemNeural::parent(const QModelIndex &child) const
{
NeuralNode* node = nodeFromIndex(child);
if (node == 0)
{
return QModelIndex();
}
NeuralNode* parentNode = node->getParent();
if (parentNode == 0)
{
return QModelIndex();
}
NeuralNode* grandParentNode = parentNode->getParent();
if (grandParentNode == 0)
{
return QModelIndex();
}
int row = grandParentNode->getInputs().indexOf(parentNode);
return createIndex(row, 0, parentNode);
}
int ModelItemNeural::rowCount(const QModelIndex& parent) const
{
if (parent.isValid() == false)
{
return 0;
}
if (parent.column() > 0)
{
return 0;
}
NeuralNode* parentNode = nodeFromIndex(parent);
if (parentNode == 0)
{
return 0;
}
return parentNode->getInputs().length();
}
int ModelItemNeural::columnCount(const QModelIndex &parent) const
{
return 2;
}
QVariant ModelItemNeural::data(const QModelIndex &index, int role) const
{
qDebug() << "Data";
if (index.isValid() == false)
{
return QVariant();
}
if (role != Qt::DisplayRole)
{
return QVariant();
}
NeuralNode* node = nodeFromIndex(index);
if (node == 0)
{
return QVariant();
}
switch (index.column())
{
case 0:
{
// Stripping the name of the NeuralNode type.
QString name = typeid(node).name();
int index = name.indexOf(" ");
if (index >= 0)
{
name = name.remove(0, index + 1);
}
qDebug() << "Name Column";
return "Test";
return name;
}
case 1:
{
qDebug() << "Value Column";
return node->getWeight();
}
}
return QVariant();
}
QVariant ModelItemNeural::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
{
switch (section)
{
case 0:
{
return "Node";
}
case 1:
{
return "Weight";
}
}
}
return QVariant();
}
NeuralNode * ModelItemNeural::nodeFromIndex(const QModelIndex &index) const
{
if (index.isValid() == true)
{
//return (NeuralNode*)(index.internalPointer());
return static_cast<NeuralNode *>(index.internalPointer());
}
else
{
return rootNode;
}
}
void ModelItemNeural::setRootNode(NeuralNode *rootNode)
{
delete this->rootNode;
this->rootNode = rootNode;
reset();
}
The code from the MainWindow where the view is located is below.
#include "MainWindow.h"
#include "ui_MainWindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
NeuralNetwork* network = new NeuralNetwork();
modelNeural = new ModelItemNeural();
modelNeural->setRootNode(network);
ui->treeView->setModel(modelNeural);
update();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_actionNew_triggered()
{
NeuralNetwork* network = new NeuralNetwork();
modelNeural->setRootNode(network);
ui->treeView->update();
}
I should mention that the header does display for this model. However, even when I set an item, nothing is displayed in the widget save the header.
Oh and NeuralNetwork is a sub of NeuralNode.
The problem is this fragment:
int ModelItemNeural::rowCount(const QModelIndex& parent) const
{
if (parent.isValid() == false)
{
return 0;
}
You're basically saying that the root node (indicated by invalid parent index) has zero children i.e. the model has zero top-level rows. So the view queries no further.
Just drop this check and it should work. nodeFromIndex seems to handle root node correctly.
Did you add the model (not the item) to the treeview control?
Did you create items of your derived type and add them to the model?
data() should be called if your model is being accessed.
You have to override the following method in your QAbstractItemModel inherited class:
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
Then write this within:
return createIndex(row, column, nullptr);
Something like this:
QModelIndex index(int row, int column, const QModelIndex &parent) const {
return createIndex(row, column, nullptr);
}
Related
I'm trying to display a user defined tree Family_tree through a TreeModel class derived from QAbstractItemModel containing a Family_tree ft_ member and the overrides for the index, parent, data etc methods.
I rewrote the methods using the Family_tree nodes and I'm calling them from MainWindow like shown below (I double checked Family_tree and it is being set up correctly). All I see is empty space with a single '1' appearing on the top left corner since I have columnCount return 1 for all the nodes and I'm starting by trying to display a tree with only one node (I only expect icons to show up).
Am I misinterpreting how this works in any way? I would really appreciate some insight, thank you for your time!
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
inh_pattern = AD;
patient = new Patient("Nancy Kalaj", Date(12, 12, 1997), 25, 'F', Genotype("DD"), Date());
family = new Family_tree(*patient);
selected = new SelectedPatient(); selected->setSelectedPatient(patient);
patientOverview = new PatientOverview(*patient, *selected, this); patientOverview->show();
patientRelatives = new PatientRelatives(*family, *selected, this);
treeModel = new TreeModel(*family);
QTreeView* view = new QTreeView(this);
view->setModel(treeModel);
//...
}
QModelIndex TreeModel::index(int row, int column, const QModelIndex& parent) const {
if (!hasIndex(row, column, parent)) {return QModelIndex();}
node* parentNode;
if (!parent.isValid()) {parentNode = ft_.get_root();}
else {parentNode = static_cast<node*>(parent.internalPointer());}
node* childNode = parentNode->getChildren()[row];
if (childNode) {return createIndex(row, column, childNode);}
else {return QModelIndex();}
}
QModelIndex TreeModel::parent(const QModelIndex& index) const {
if (!index.isValid()) {return QModelIndex();}
node* n = static_cast<node*>(index.internalPointer());
node* parentNode = n->getParent();
if (parentNode == ft_.get_root()) {return QModelIndex();}
return createIndex(row(parentNode), 0, parentNode);
}
bool TreeModel::hasChildren(const QModelIndex& parent) const {
if (!parent.isValid()) {
return true;
}
node* n = static_cast<node*>(parent.internalPointer());
return !n->getChildren().empty();
}
int TreeModel::rowCount(const QModelIndex& parent) const {
node* parentNode;
if (parent.column() > 0) {return 0;}
if (!parent.isValid()) {parentNode = ft_.get_root();}
else {parentNode = static_cast<node*>(parent.internalPointer());}
return parentNode->getChildren().size();
}
int TreeModel::columnCount(const QModelIndex& parent) const {
return 1;
}
QVariant TreeModel::data(const QModelIndex &index, int role) const {
if (!index.isValid()) {return QVariant();}
node* n = static_cast<node*>(index.internalPointer());
if (role == Qt::DecorationRole) {
if (index.column() == 0) {
QPixmap pixmap(16, 16);
pixmap.fill(Qt::black);
QPainter painter(&pixmap);
painter.setBrush(Qt::white);
painter.drawRect(0, 0, 15, 15);
return QIcon(pixmap);
}
else {return QVariant();}
}
return QVariant();
}
Qt::ItemFlags TreeModel::flags(const QModelIndex& index) const {
if (!index.isValid()) {return Qt::NoItemFlags;}
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}
I'm using a QTableView to view some data from sql database.
the structure is as follows :
QSqlQueryModel
subclass of QSortFilterProxyModel that's used to filter data through search box
QTableView and it's model is the proxy model.
Sometimes when I search and the FilterAcceptsRow is called, the view doesn't load the data, surprisingly when I resize the window or click on the header to sort it, the data gets loaded !
bool ProxyModel::filterAcceptsRow(int source_row,
const QModelIndex &source_parent) const
{
QModelIndex indName = sourceModel()->index(source_row,
7, source_parent);
QModelIndex indNumber= sourceModel()->index(source_row,
6, source_parent);
QModelIndex indAgency = sourceModel()->index(source_row,
0, source_parent);
QModelIndex indStartDate = sourceModel()->index(source_row,2,source_parent);
QModelIndex indEndDate = sourceModel()->index(source_row,1,source_parent);
if (searchBy == 0) // search by name
{
if(sourceModel()->data(indName).toString().contains(name_))
return true;
else
return false;
}
else if( searchBy == 1) // search by number
{
if(sourceModel()->data(indNumber).toString().toLower().contains(number_.toLower()))
return true;
else
return false;
}
else if (searchBy == 2) // search by agency
{
return agencyList.indexOf(sourceModel()->data(indAgency).toString()) == agency_ ;
}
else if (searchBy == 3) // search By date
{
if (sourceModel()->data(indStartDate).toDate() >= start_ &&
sourceModel()->data(indEndDate).toDate() <= end_)
return true;
}
return false;
}
Is there someway to get this working properly ?
I found the fix for that issue, It turns out that the QSortFilterProxyModel does not load all the data until you scroll the QTableView down, then it starts to load the rest of the data.
and that might be a good thing if there is a lot of data, so it's on by default.
I got it working the way i needed by using
bool QAbstractItemModel::canFetchMore(const QModelIndex &parent) const
and
void QAbstractItemModel::fetchMore(const QModelIndex &parent)
after each call for invalidateFilter();
I call them to fetch all the data
while (canFetchMore(sourceModel()->index(rowCount()-1,0)))
{
fetchMore(sourceModel()->index(rowCount()-1,0));
}
Here is a working snippet from my project
#include "qqmlsortfilterproxymodel.h"
#include <QtQml>
#include <algorithm>
#include "filter.h"
#include "sorter.h"
QQmlSortFilterProxyModel::QQmlSortFilterProxyModel(QObject *parent) :
QSortFilterProxyModel(parent)
{
connect(this, &QAbstractProxyModel::sourceModelChanged, this, &QQmlSortFilterProxyModel::updateRoles);
connect(this, &QAbstractItemModel::modelReset, this, &QQmlSortFilterProxyModel::updateRoles);
connect(this, &QAbstractItemModel::rowsInserted, this, &QQmlSortFilterProxyModel::countChanged);
connect(this, &QAbstractItemModel::rowsRemoved, this, &QQmlSortFilterProxyModel::countChanged);
connect(this, &QAbstractItemModel::modelReset, this, &QQmlSortFilterProxyModel::countChanged);
connect(this, &QAbstractItemModel::layoutChanged, this, &QQmlSortFilterProxyModel::countChanged);
setDynamicSortFilter(true);
}
int QQmlSortFilterProxyModel::count() const
{
return rowCount();
}
const QString& QQmlSortFilterProxyModel::filterRoleName() const
{
return m_filterRoleName;
}
void QQmlSortFilterProxyModel::setFilterRoleName(const QString& filterRoleName)
{
if (m_filterRoleName == filterRoleName)
return;
m_filterRoleName = filterRoleName;
updateFilterRole();
Q_EMIT filterRoleNameChanged();
}
QString QQmlSortFilterProxyModel::filterPattern() const
{
return filterRegExp().pattern();
}
void QQmlSortFilterProxyModel::setFilterPattern(const QString& filterPattern)
{
QRegExp regExp = filterRegExp();
if (regExp.pattern() == filterPattern)
return;
regExp.setPattern(filterPattern);
QSortFilterProxyModel::setFilterRegExp(regExp);
Q_EMIT filterPatternChanged();
}
QQmlSortFilterProxyModel::PatternSyntax QQmlSortFilterProxyModel::filterPatternSyntax() const
{
return static_cast<PatternSyntax>(filterRegExp().patternSyntax());
}
void QQmlSortFilterProxyModel::setFilterPatternSyntax(QQmlSortFilterProxyModel::PatternSyntax patternSyntax)
{
QRegExp regExp = filterRegExp();
QRegExp::PatternSyntax patternSyntaxTmp = static_cast<QRegExp::PatternSyntax>(patternSyntax);
if (regExp.patternSyntax() == patternSyntaxTmp)
return;
regExp.setPatternSyntax(patternSyntaxTmp);
QSortFilterProxyModel::setFilterRegExp(regExp);
Q_EMIT filterPatternSyntaxChanged();
}
const QVariant& QQmlSortFilterProxyModel::filterValue() const
{
return m_filterValue;
}
void QQmlSortFilterProxyModel::setFilterValue(const QVariant& filterValue)
{
if (m_filterValue == filterValue)
return;
m_filterValue = filterValue;
invalidateFilter();
Q_EMIT filterValueChanged();
}
const QString& QQmlSortFilterProxyModel::sortRoleName() const
{
return m_sortRoleName;
}
void QQmlSortFilterProxyModel::setSortRoleName(const QString& sortRoleName)
{
if (m_sortRoleName == sortRoleName)
return;
m_sortRoleName = sortRoleName;
updateSortRole();
Q_EMIT sortRoleNameChanged();
}
bool QQmlSortFilterProxyModel::ascendingSortOrder() const
{
return m_ascendingSortOrder;
}
void QQmlSortFilterProxyModel::setAscendingSortOrder(bool ascendingSortOrder)
{
if (m_ascendingSortOrder == ascendingSortOrder)
return;
m_ascendingSortOrder = ascendingSortOrder;
Q_EMIT ascendingSortOrderChanged();
invalidate();
}
QQmlListProperty<Filter> QQmlSortFilterProxyModel::filters()
{
return QQmlListProperty<Filter>(this, &m_filters,
&QQmlSortFilterProxyModel::append_filter,
&QQmlSortFilterProxyModel::count_filter,
&QQmlSortFilterProxyModel::at_filter,
&QQmlSortFilterProxyModel::clear_filters);
}
QQmlListProperty<Sorter> QQmlSortFilterProxyModel::sorters()
{
return QQmlListProperty<Sorter>(this, &m_sorters,
&QQmlSortFilterProxyModel::append_sorter,
&QQmlSortFilterProxyModel::count_sorter,
&QQmlSortFilterProxyModel::at_sorter,
&QQmlSortFilterProxyModel::clear_sorters);
}
void QQmlSortFilterProxyModel::classBegin()
{
}
void QQmlSortFilterProxyModel::componentComplete()
{
m_completed = true;
for (const auto& filter : m_filters)
filter->proxyModelCompleted();
invalidate();
sort(0);
}
QVariant QQmlSortFilterProxyModel::sourceData(const QModelIndex& sourceIndex, const QString& roleName) const
{
int role = sourceModel()->roleNames().key(roleName.toUtf8());
return sourceData(sourceIndex, role);
}
QVariant QQmlSortFilterProxyModel::sourceData(const QModelIndex &sourceIndex, int role) const
{
return sourceModel()->data(sourceIndex, role);
}
int QQmlSortFilterProxyModel::roleForName(const QString& roleName) const
{
return roleNames().key(roleName.toUtf8(), -1);
}
QVariantMap QQmlSortFilterProxyModel::get(int row) const
{
QVariantMap map;
QModelIndex modelIndex = index(row, 0);
QHash<int, QByteArray> roles = roleNames();
for (QHash<int, QByteArray>::const_iterator it = roles.begin(); it != roles.end(); ++it)
map.insert(it.value(), data(modelIndex, it.key()));
return map;
}
QVariant QQmlSortFilterProxyModel::get(int row, const QString& roleName) const
{
return data(index(row, 0), roleForName(roleName));
}
QModelIndex QQmlSortFilterProxyModel::mapToSource(const QModelIndex& proxyIndex) const
{
return QSortFilterProxyModel::mapToSource(proxyIndex);
}
int QQmlSortFilterProxyModel::mapToSource(int proxyRow) const
{
QModelIndex proxyIndex = index(proxyRow, 0);
QModelIndex sourceIndex = mapToSource(proxyIndex);
return sourceIndex.isValid() ? sourceIndex.row() : -1;
}
QModelIndex QQmlSortFilterProxyModel::mapFromSource(const QModelIndex& sourceIndex) const
{
return QSortFilterProxyModel::mapFromSource(sourceIndex);
}
int QQmlSortFilterProxyModel::mapFromSource(int sourceRow) const
{
QModelIndex proxyIndex;
if (QAbstractItemModel* source = sourceModel()) {
QModelIndex sourceIndex = source->index(sourceRow, 0);
proxyIndex = mapFromSource(sourceIndex);
}
return proxyIndex.isValid() ? proxyIndex.row() : -1;
}
bool QQmlSortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const
{
if (!m_completed)
return true;
QModelIndex sourceIndex = sourceModel()->index(source_row, 0, source_parent);
bool valueAccepted = !m_filterValue.isValid() || ( m_filterValue == sourceModel()->data(sourceIndex, filterRole()) );
bool baseAcceptsRow = valueAccepted && QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent);
baseAcceptsRow = baseAcceptsRow && std::all_of(m_filters.begin(), m_filters.end(),
[=, &source_parent] (Filter* filter) {
return filter->filterAcceptsRow(sourceIndex);
}
);
return baseAcceptsRow;
}
bool QQmlSortFilterProxyModel::lessThan(const QModelIndex& source_left, const QModelIndex& source_right) const
{
if (m_completed) {
if (!m_sortRoleName.isEmpty()) {
if (QSortFilterProxyModel::lessThan(source_left, source_right))
return m_ascendingSortOrder;
if (QSortFilterProxyModel::lessThan(source_right, source_left))
return !m_ascendingSortOrder;
}
for(auto sorter : m_sorters) {
if (sorter->enabled()) {
int comparison = sorter->compareRows(source_left, source_right);
if (comparison != 0)
return comparison < 0;
}
}
}
return source_left.row() < source_right.row();
}
void QQmlSortFilterProxyModel::resetInternalData()
{
QSortFilterProxyModel::resetInternalData();
if (sourceModel() && QSortFilterProxyModel::roleNames().isEmpty()) { // workaround for when a model has no roles and roles are added when the model is populated (ListModel)
// QTBUG-57971
connect(sourceModel(), &QAbstractItemModel::rowsInserted, this, &QQmlSortFilterProxyModel::initRoles);
connect(this, &QAbstractItemModel::rowsAboutToBeInserted, this, &QQmlSortFilterProxyModel::initRoles);
}
}
void QQmlSortFilterProxyModel::invalidateFilter()
{
if (m_completed)
QSortFilterProxyModel::invalidateFilter();
}
void QQmlSortFilterProxyModel::invalidate()
{
if (m_completed)
QSortFilterProxyModel::invalidate();
}
void QQmlSortFilterProxyModel::updateFilterRole()
{
QList<int> filterRoles = roleNames().keys(m_filterRoleName.toUtf8());
if (!filterRoles.empty())
{
setFilterRole(filterRoles.first());
}
}
void QQmlSortFilterProxyModel::updateSortRole()
{
QList<int> sortRoles = roleNames().keys(m_sortRoleName.toUtf8());
if (!sortRoles.empty())
{
setSortRole(sortRoles.first());
invalidate();
}
}
void QQmlSortFilterProxyModel::updateRoles()
{
updateFilterRole();
updateSortRole();
}
void QQmlSortFilterProxyModel::initRoles()
{
disconnect(sourceModel(), &QAbstractItemModel::rowsInserted, this, &QQmlSortFilterProxyModel::initRoles);
disconnect(this, &QAbstractItemModel::rowsAboutToBeInserted, this , &QQmlSortFilterProxyModel::initRoles);
resetInternalData();
updateRoles();
}
QVariantMap QQmlSortFilterProxyModel::modelDataMap(const QModelIndex& modelIndex) const
{
QVariantMap map;
QHash<int, QByteArray> roles = roleNames();
for (QHash<int, QByteArray>::const_iterator it = roles.begin(); it != roles.end(); ++it)
map.insert(it.value(), sourceModel()->data(modelIndex, it.key()));
return map;
}
void QQmlSortFilterProxyModel::append_filter(QQmlListProperty<Filter>* list, Filter* filter)
{
if (!filter)
return;
QQmlSortFilterProxyModel* that = static_cast<QQmlSortFilterProxyModel*>(list->object);
that->m_filters.append(filter);
connect(filter, &Filter::invalidate, that, &QQmlSortFilterProxyModel::invalidateFilter);
filter->m_proxyModel = that;
that->invalidateFilter();
}
int QQmlSortFilterProxyModel::count_filter(QQmlListProperty<Filter>* list)
{
QList<Filter*>* filters = static_cast<QList<Filter*>*>(list->data);
return filters->count();
}
Filter* QQmlSortFilterProxyModel::at_filter(QQmlListProperty<Filter>* list, int index)
{
QList<Filter*>* filters = static_cast<QList<Filter*>*>(list->data);
return filters->at(index);
}
void QQmlSortFilterProxyModel::clear_filters(QQmlListProperty<Filter> *list)
{
QQmlSortFilterProxyModel* that = static_cast<QQmlSortFilterProxyModel*>(list->object);
that->m_filters.clear();
that->invalidateFilter();
}
void QQmlSortFilterProxyModel::append_sorter(QQmlListProperty<Sorter>* list, Sorter* sorter)
{
if (!sorter)
return;
auto that = static_cast<QQmlSortFilterProxyModel*>(list->object);
that->m_sorters.append(sorter);
connect(sorter, &Sorter::invalidate, that, &QQmlSortFilterProxyModel::invalidate);
sorter->m_proxyModel = that;
that->invalidate();
}
int QQmlSortFilterProxyModel::count_sorter(QQmlListProperty<Sorter>* list)
{
auto sorters = static_cast<QList<Sorter*>*>(list->data);
return sorters->count();
}
Sorter* QQmlSortFilterProxyModel::at_sorter(QQmlListProperty<Sorter>* list, int index)
{
auto sorters = static_cast<QList<Sorter*>*>(list->data);
return sorters->at(index);
}
void QQmlSortFilterProxyModel::clear_sorters(QQmlListProperty<Sorter>* list)
{
auto that = static_cast<QQmlSortFilterProxyModel*>(list->object);
that->m_sorters.clear();
that->invalidate();
}
int QQmlSortFilterProxyModel::getRowCount() const
{
return rowCount();
}
void registerQQmlSortFilterProxyModelTypes() {
qmlRegisterType<QQmlSortFilterProxyModel>("SortFilterProxyModel", 0, 2, "SortFilterProxyModel");
}
Q_COREAPP_STARTUP_FUNCTION(registerQQmlSortFilterProxyModelTypes)
I have a custom QAbstractTableModel and a proxy model which flips the axes of the first model. With the table this works, I just switch the model. When I switch to my proxy model for the chart it crashes at the point where I assign the rows to the QHXYModelMapper. What have I screwed up?
This is the table model:
#include "tablemodel.h"
TableModel::TableModel(QObject *parent) :
QAbstractTableModel(parent)
{
}
int TableModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return m_data.count();
}
int TableModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
if(m_data.count() < 1)
{
return 0;
}
return m_data[0].count();
}
QVariant TableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role != Qt::DisplayRole)
return QVariant();
if (orientation == Qt::Horizontal) {
if (section % 2 == 0)
return "x";
else
return "y";
} else {
return QString("%1").arg(section + 1);
}
}
QVariant TableModel::data(const QModelIndex &index, int role) const
{
if (!role == Qt::DisplayRole)
{
return QVariant();
}
return m_data[index.row()].at(index.column());
}
bool TableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (index.isValid() && role == Qt::EditRole) {
m_data[index.row()].replace(index.column(), value.toDouble());
emit dataChanged(index, index);
return true;
}
return false;
}
void TableModel::appendRow(QVector<double> row)
{
emit layoutAboutToBeChanged();
emit beginInsertRows(QModelIndex(), rowCount(), rowCount());
m_data.append(row);
emit endInsertRows();
emit layoutChanged();
}
void TableModel::clear()
{
for(int i = 0; i < m_data.count(); ++i)
{
m_data[i].clear();
}
m_data.clear();
}
This is the proxy implementation:
HorizontalProxyModel::HorizontalProxyModel(QObject *parent) : QAbstractProxyModel(parent)
{
}
QModelIndex HorizontalProxyModel::mapToSource(const QModelIndex &proxyIndex) const
{
if (sourceModel()) {
return sourceModel()->index(proxyIndex.column(), proxyIndex.row());
} else {
return QModelIndex();
}
}
QModelIndex HorizontalProxyModel::mapFromSource(const QModelIndex &sourceIndex) const
{
return index(sourceIndex.column(), sourceIndex.row());
}
QModelIndex HorizontalProxyModel::index(int row, int column, const QModelIndex &) const
{
return createIndex(row, column, (void*) 0);
}
QModelIndex HorizontalProxyModel::parent(const QModelIndex &) const
{
return QModelIndex();
}
int HorizontalProxyModel::rowCount(const QModelIndex &) const
{
return sourceModel() ? sourceModel()->columnCount() : 0;
}
int HorizontalProxyModel::columnCount(const QModelIndex &) const
{
return sourceModel() ? sourceModel()->rowCount() : 0;
}
QVariant HorizontalProxyModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (!sourceModel()) { return QVariant(); }
Qt::Orientation new_orientation = orientation == Qt::Horizontal ?
Qt::Vertical : Qt::Horizontal;
return sourceModel() ? sourceModel()->headerData(section, new_orientation, role) : 0;
}
QVariant HorizontalProxyModel::data(const QModelIndex &index) const
{
qDebug() << "h model data";
return sourceModel() ? sourceModel()->data(sourceModel()->index(index.column(), index.row())) : 0;
}
And this is where I assign the model:
void MainWindow::plot(QAbstractItemModel* m)
{
QChart* chart = new QChart;
qDebug() << m->rowCount() << " " << m->columnCount();
for(int row = 0; row < m->rowCount(); ++row)
{
QLineSeries *series = new QLineSeries;
QHXYModelMapper* mapper = new QHXYModelMapper;
QString name = "Row " + QString::number(row);
series->setName(name);
mapper->setModel(m);
mapper->setSeries(series);
mapper->setXRow(row); //crashes here if proxy model
mapper->setYRow(row);
chart->addSeries(series);
}
chart->createDefaultAxes();
QChart* oldChart = chartView->chart();
chartView->setChart(chart);
oldChart->deleteLater();
}
EDIT
Some more info...
Looking at the debugger, it seems that the index being created in the ProxyModel and passed to the original model is -1/-1 (invalid). Does line 9 in this debug output mean that the base class QAbstractProxyModel::data() is being called, instead of the one from my derived proxy model? If so, why?
1 __pthread_kill
0x7fff91eaff06
2 pthread_kill 0x7fff907204ec
3 abort 0x7fff876cd6df
4 qt_message_fatal(QtMsgType, QMessageLogContext const&, QString const&) 0x100ac3e79
5 QMessageLogger::fatal(const char *, ...) const 0x100ac5847
6 qt_assert_x(const char *, const char *, const char *, int) 0x100ac0682
7 QList<QVector<double>>::operator[](int) const qlist.h 541 0x10000fced
8 TableModel::data(QModelIndex const&, int) const tablemodel.cpp 46 0x10000f8f1
9 QAbstractProxyModel::data(QModelIndex const&, int) const 0x100c4c28b
10 QtCharts::QXYModelMapperPrivate::valueFromModel(QModelIndex) 0x10171dfb3
11 QtCharts::QXYModelMapperPrivate::initializeXYFromModel() 0x10171d92f
12 QtCharts::QHXYModelMapper::setYRow(int) 0x101720bf4
13 MainWindow::plot(QAbstractItemModel *)
UDPATE: FIX
So, the fix I have right now is to call QHXYModelMapper::setColumnCount() manually, like so:
mapper->setModel(m);
mapper->setSeries(series);
mapper->setColumnCount(m->columnCount());
mapper->setXRow(row);
mapper->setYRow(row);
The docs seem to imply that the default value will use the total number of columns in the model, if I don't explicitly set this:
http://doc.qt.io/qt-5/qhxymodelmapper.html#columnCount-prop
Talked it over on the Qt forums a bit, this appears to be a bug, where the default value for the HXYModelMapper columnCount property does not actually retrieve the proper columnCount from the model. The workaround is to call setColumnCount yourself and set it to the columnCount of your model.
mapper->setModel(m);
mapper->setSeries(series);
mapper->setColumnCount(m->columnCount());
mapper->setXRow(row);
mapper->setYRow(row);
Bug report: https://bugreports.qt.io/browse/QTBUG-57342
I am using QAbstractTableModel and ComboBoxItemDelegate which inherits QStyledItemDelegate. I am able to populate my data in combo boxes in table view but by default nothing is displayed but after clicking correct data is visible in dropdown. How can i set first item as a default item and display it?
My Combox delegate:
class ComboBoxItemDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
ComboBoxItemDelegate(QObject* parent = 0);
~ComboBoxItemDelegate();
virtual QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const;
virtual void setEditorData(QWidget* editor, const QModelIndex& index) const;
virtual void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const;
virtual void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const;
};
Create editor function:
QWidget* ComboBoxItemDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
QComboBox* cb = new QComboBox(parent);
QStringList destList = SFilterEditorData::instance().getDestinationList();
cb->addItem(QString("All"));
for (int i = 0; i < destList.size(); i++)
{
cb->addItem(destList.at(i));
}
cb->setEditable(true);
return cb;
}
set Editor function:
void ComboBoxItemDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const
{
if (QComboBox* cb = qobject_cast<QComboBox*>(editor)) {
//QString currentText = index.data(Qt::EditRole).toString();
QString currentText = index.model()->data(index, Qt::DisplayRole).toString();
int cbIndex = cb->findText(currentText);
// if it is valid, adjust the combobox
if (cbIndex >= 0)
cb->setCurrentIndex(cbIndex);
}
else {
QStyledItemDelegate::setEditorData(editor, index);
}
}
Inserting rows in my model:
bool STableModel::insertRows(int position, int rows, const QModelIndex &parent)
{
int columns = columnCount();
beginInsertRows(parent, position, position + rows - 1);
for (int row = 0; row < rows; ++row) {
QStringList items;
for (int column = 0; column < columns; ++column)
{
items.append(""); // This might be the reason for this issue.
}
rowList.insert(position, items);
}
endInsertRows();
return true;
}
Setting data in model:
QVariant STableModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (role == Qt::DisplayRole)
return rowList[index.row()][index.column()];
else if (role == Qt::CheckStateRole && index.column() == 0)
{
int status = SFilterEditorData::instance().getStatus(index.row());
if (status)
return Qt::Checked;
else
return Qt::Unchecked;
}
else
return QVariant();
}
bool STableModel::setData(const QModelIndex &index,
const QVariant &value, int role)
{
if (!index.isValid() /*|| role != Qt::EditRole*/)
return false;
if (role == Qt::CheckStateRole)
{
if ((Qt::CheckState)value.toInt() == Qt::Checked)
{
SFilterEditorData::instance().setStatus(index.row(),1);
return true;
}
else
{
SFilterEditorData::instance().setStatus(index.row(), 0);
return true;
}
}
i have referred this link: http://www.qtcentre.org/threads/39194-setEditorData()-for-QComboBox-Delegate
I have followed the editableTreeView example provided with Qt and I'm having an interesting problem. Top level items can be added correctly but if I am to give one of them a child, its a pointer to the first top level item.
My code for the QAbstractItemModel is below.
#include "ModelItemNeural.h"
ModelItemNeural::ModelItemNeural(QObject *parent)
: QAbstractItemModel(parent)
{
counta = 0;
rootNode = new NeuralNode();
addNeuralNode(NeuralNode::NEURAL_NETWORK, 0, 0);
}
QModelIndex ModelItemNeural::index(int row, int column, const QModelIndex &parent) const
{
// Out of bounds and null rootNode check.
// if (rootNode == 0 || row < 0 || column < 0)
// {
// return QModelIndex();
// }
if (parent.isValid() && parent.column() != 0)
{
return QModelIndex();
}
NeuralNode* parentNode = nodeFromIndex(parent);
NeuralNode* childNode = parentNode->getInputs().value(row);
if (childNode == 0)
{
return QModelIndex();
}
return createIndex(row, column, childNode);
}
QModelIndex ModelItemNeural::parent(const QModelIndex &child) const
{
NeuralNode* node = nodeFromIndex(child);
if (node == 0)
{
return QModelIndex();
}
NeuralNode* parentNode = node->getParent();
if (parentNode == 0)
{
return QModelIndex();
}
NeuralNode* grandParentNode = parentNode->getParent();
if (grandParentNode == 0)
{
return QModelIndex();
}
int row = grandParentNode->getInputs().indexOf(parentNode);
return createIndex(row, 0, parentNode);
}
QVariant ModelItemNeural::data(const QModelIndex &index, int role) const
{
if (index.isValid() == false)
{
return QVariant();
}
if (role != Qt::DisplayRole)
{
return QVariant();
}
NeuralNode* node = nodeFromIndex(index);
if (node == 0)
{
return QVariant();
}
switch (index.column())
{
case 0:
{
// Stripping the name of the NeuralNode type.
QString name = typeid(node).name();
int index = name.indexOf(" ");
if (index >= 0)
{
name = name.remove(0, index + 1);
}
//return "Test";
return node->getId();
return name;
}
case 1:
{
return node->getWeight();
}
}
return QVariant();
}
QVariant ModelItemNeural::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
{
switch (section)
{
case 0:
{
return "Node";
}
case 1:
{
return "Weight";
}
}
}
return QVariant();
}
int ModelItemNeural::rowCount(const QModelIndex &parent) const
{
NeuralNode *parentItem = nodeFromIndex(parent);
return parentItem->childCount();
}
int ModelItemNeural::columnCount(const QModelIndex &parent) const
{
return rootNode->columnCount();
}
NeuralNode * ModelItemNeural::nodeFromIndex(const QModelIndex &index) const
{
if (index.isValid() == true)
{
return static_cast<NeuralNode *>(index.internalPointer());
}
else
{
return rootNode;
}
}
void ModelItemNeural::setRootNode(NeuralNode *rootNode)
{
delete this->rootNode;
this->rootNode = rootNode;
reset();
}
Qt::ItemFlags ModelItemNeural::flags(const QModelIndex &index) const
{
if (!index.isValid())
{
return 0;
}
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}
bool ModelItemNeural::insertColumns(int position, int columns, const QModelIndex &parent)
{
bool success;
beginInsertColumns(parent, position, position + columns - 1);
success = rootNode->insertColumns(position, columns);
endInsertColumns();
return success;
}
bool ModelItemNeural::removeColumns(int position, int columns, const QModelIndex &parent)
{
bool success;
beginRemoveColumns(parent, position, position + columns - 1);
success = rootNode->removeColumns(position, columns);
endRemoveColumns();
if (rootNode->columnCount() == 0)
{
removeRows(0, rowCount());
}
return success;
}
bool ModelItemNeural::insertRows(int position, int rows, const QModelIndex &parent)
{
NeuralNode *parentItem = nodeFromIndex(parent);
counta++;
bool success;
beginInsertRows(parent, position, position + rows - 1);
switch (addedNode)
{
case NeuralNode::NEURALNODE:
{
default:
break;
}
case NeuralNode::NEURON:
{
success = parentItem->insertChildren(position, rows, new Neuron(NeuralNode::NEURON, counta + 100, counta));
break;
}
case NeuralNode::NEURAL_NETWORK:
{
success = parentItem->insertChildren(position, rows, new NeuralNetwork());
break;
}
case NeuralNode::SENSOR_INT:
{
success = parentItem->insertChildren(position, rows, new SensorInt());
break;
}
case NeuralNode::SENSOR_DOUBLE:
{
success = parentItem->insertChildren(position, rows, new SensorDouble());
break;
}
}
endInsertRows();
return success;
}
bool ModelItemNeural::removeRows(int position, int rows, const QModelIndex &parent)
{
NeuralNode *parentItem = nodeFromIndex(parent);
bool success = true;
beginRemoveRows(parent, position, position + rows - 1);
success = parentItem->removeChildren(position, rows);
endRemoveRows();
return success;
}
void ModelItemNeural::addNeuralNode(const NeuralNode::NeuralType& type, int position, int columns, const QModelIndex &parent)
{
addedNode = type;
if (columnCount(parent) == 0)
{
if (insertColumn(0, parent) == false)
{
return;
}
}
if (insertRow(0, parent) == false)
{
return;
}
//insertRows(position, columns, parent);
}
void ModelItemNeural::removeNeuralNode(const NeuralNode::NeuralType& type, int position, int columns, const QModelIndex &parent)
{
}
The code for the NeuralNode (items for the tree) is shown below
#include "NeuralNode.h"
NeuralNode::NeuralNode(NeuralNode *parent)
: id(0), type(NeuralNode::NEURALNODE), weight(0), parent(parent)
{
}
NeuralNode::NeuralNode(const NeuralType &type, NeuralNode *parent)
: id(id), type(type), weight(0), parent(parent)
{
}
NeuralNode::NeuralNode(const NeuralType &type, const int &id, NeuralNode *parent)
: id(id), type(type), weight(weight), parent(parent)
{
}
NeuralNode::NeuralNode(const NeuralType &type, const int &id, const double &weight, NeuralNode *parent)
: id(id), type(type), weight(weight), parent(parent)
{
}
bool NeuralNode::operator ==(const NeuralNode &node) const
{
if (this->id != node.id) // The id of this Neuron.
{
return false;
}
else if (weight != node.weight) // The weight of this Neuron.
{
return false;
}
else if (inputs != node.inputs) // The inputs to this NeuralNode.
{
return false;
}
else if (parent != node.parent) // The parent of the NeuralNode.
{
return false;
}
else
{
return true;
}
}
NeuralNode * NeuralNode::getParent() const
{
return parent;
}
void NeuralNode::setParent(NeuralNode *parent)
{
this->parent = parent;
}
QList<NeuralNode*> NeuralNode::getInputs() const
{
return inputs;
}
void NeuralNode::setInputs(const QList<NeuralNode*> &inputs)
{
this->inputs = inputs;
}
NeuralNode * NeuralNode::child(int number)
{
return inputs.value(number);
}
int NeuralNode::childCount() const
{
return inputs.count();
}
int NeuralNode::columnCount() const
{
return 2;
}
bool NeuralNode::insertChildren(int position, int count, NeuralNode* node)
{
if (position < 0 || position > inputs.length())
{
return false;
}
for (int row = 0; row < count; ++row)
{
inputs.insert(position, node);
}
return true;
}
bool NeuralNode::removeChildren(int position, int count)
{
if (position < 0 || position + count > inputs.length())
{
return false;
}
for (int row = 0; row < count; ++row)
{
delete inputs.takeAt(position);
}
return true;
}
int NeuralNode::childNumber() const
{
return inputs.length();
}
bool NeuralNode::insertColumns(int position, int columns)
{
if (position < 0)
{
return false;
}
for (int a = 0; a < inputs.length(); ++a)
{
inputs.at(a)->insertColumns(position, columns);
}
return true;
}
bool NeuralNode::removeColumns(int position, int columns)
{
if (position < 0)
{
return false;
}
for (int a = 0; inputs.length(); ++a)
{
inputs.at(a)->removeColumns(position, columns);
}
return true;
}
Here is what i've noticed.
Tracing through the index() call from my model does return indices for children of top level nodes.
calling ui->treeView->selectedModel()->currentIndex() always returns a invalid index (root). The same is true for ui->treeView->currentIndex().
I smell a problem with a pointer somewhere but I can't find it. Any help will be greatly appreciated.
Jec
Edit: Here is a crude graphic of the problem
Currently:
Root->A
B
C->A
What I Want:
Root->A
B
C->D
Where A, B, C and D are unique NeuralNodes. I should have no duplicates in the tree.
Thanks again.
it turns out of I forgot to link the children to their parents. Without a valid parent() the top level nodes are always added.