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.
Related
I have implemented a custom QAbstractTableModel and I have run it through the QAbstractItemModelTester and there are no more issues in my model. However, I am now trying to implement sorting through a QSortFilterProxyModel and I can't seem to get anything working at all.
void RDMSensorModels::UpdateDevice(ArtNet::ArtRdmDevice* rdmDev, const RDM::RDMProcessor::RDMDeviceModel& model, int pid) {
if (s_RequiredPIDs.contains(pid)) {
for (int i = 0; i < m_RDMDevices.size(); i++) {
if (m_RDMDevices[i] == rdmDev) {
emit dataChanged(createIndex(i, 0), createIndex(i, columnCount() - 1));
return;
}
}
}
}
This is the function, which emits the models dataChanged signal and I dont think there is a problem here, but after this signal is emitted the program crashes inside QSortFilterProxyModels internal dataChanged handler
As I can't embed pictures in my questions yet, here is a link to where the debugger breaks inside QSortFilterProxyModel
The weirdest thing about this is, that no matter what I pass to the dataChanged signal, the proxy_columns inside QSortFilterProxyModel is always empty.
Here you can see in the debugger, that the container is empty
If it's any help, here is my QSortFilterProxyModel implementation, its completely empty basically.
class RDMSensorSortFilterProxyModel final : public QSortFilterProxyModel {
enum SortValue {
MANUFACTUER_MODEL,
UNIVERSE_DMXADDRESS,
};
public:
RDMSensorSortFilterProxyModel(RDMSensorModels *sourceModel, QObject *parent = nullptr) : QSortFilterProxyModel(parent) {
setSourceModel(sourceModel);
}
int SortIndex();
void SetSortIndex(int value);
protected:
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
private:
SortValue m_SortValue = MANUFACTUER_MODEL;
};
int RDMSensorSortFilterProxyModel::SortIndex() { return m_SortValue; }
void RDMSensorSortFilterProxyModel::SetSortIndex(int value) {
m_SortValue = static_cast<SortValue>(value);
invalidate();
}
bool RDMSensorSortFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const { return true; }
bool RDMSensorSortFilterProxyModel::lessThan(const QModelIndex& left, const QModelIndex& right) const {
auto leftDeviceManufacturer = sourceModel()->data(left, RDMSensorModels::Roles::DeviceManufacturerRole).toString();
auto rightDeviceManufacturer = sourceModel()->data(right, RDMSensorModels::Roles::DeviceManufacturerRole).toString();
auto same = QString::compare(leftDeviceManufacturer, rightDeviceManufacturer, Qt::CaseInsensitive) == 0;
return same;
}
Here are my QAbstractTableModel reimplemented functions
QVariant RDMSensorModels::headerData(int section, Qt::Orientation orientation, int role) const {
if (section < 1)
return QString("Device");
else
return QString("Sensor %1").arg(section);
}
int RDMSensorModels::rowCount(const QModelIndex& parent) const {
if (parent.isValid())
return 0;
return m_RDMDevices.count();
}
int RDMSensorModels::columnCount(const QModelIndex& parent) const {
if (parent.isValid())
return 0;
return m_ColumnCount;
}
QVariant RDMSensorModels::data(const QModelIndex& index, int role) const {
if (!index.isValid())
return {};
int deviceIndex = index.row();
switch (role) {
case SensorGraphReadingsRole: {
auto& readings = m_RDMDevices[deviceIndex]->Sensors()[index.column() - 1]->LastReadings();
auto maxElement = f_SensorMaxReading(index.row(), index.column() - 1);
auto minElement = f_SensorMinReading(index.row(), index.column() - 1);
QVariantList values;
for (int i = 0; i < readings.size(); i++) {
values.push_back(Utils::Math::map(readings[i], maxElement, minElement, 0, 1));
}
return values;
}
case SensorMinReadingRole: return f_SensorMinReading(deviceIndex, index.column() - 1);
case SensorMaxReadingRole: return f_SensorMaxReading(deviceIndex, index.column() - 1);
case DeviceUIDRole: return f_DeviceUIDString(deviceIndex);
case DeviceUniverseRole: return f_DeviceUniverseString(deviceIndex);
case DeviceLabelRole: return f_DeviceLabelString(deviceIndex);
case DeviceManufacturerRole: return f_DeviceManufacturerString(deviceIndex);
case DeviceModelRole: return f_DeviceModelString(deviceIndex);
case SensorRangeMaxValueRole: return f_SensorRangeMaxValueString(deviceIndex, index.column() - 1);
case SensorRangeMinValueRole: return f_SensorRangeMinValueString(deviceIndex, index.column() - 1);
case SensorCurrentValueRole: return f_SensorCurrentValueString(deviceIndex, index.column() - 1);
case SensorNameRole: return f_SensorNameString(deviceIndex, index.column() - 1);
case SensorCurrentValueNormalizedRole: return f_SensorCurrentValueNormalized(deviceIndex, index.column() - 1);
case SensorMinNormalValueNormalizedRole: return f_SensorMinNormalValueNormalized(deviceIndex, index.column() - 1);
case SensorMaxNormalValueNormalizedRole: return f_SensorMaxNormalValueNormalized(deviceIndex, index.column() - 1);
case SensorValidRole: {
auto sensorCount = f_DeviceSensorCount(deviceIndex);
return sensorCount && (index.column() <= sensorCount);
}
default: return {};
}
}
QHash<int, QByteArray> RDMSensorModels::roleNames() const { return s_RoleNames; }
Any help would be greatly appreciated!
Well it turns out, trying to replicate the issue on a smaller scale made my brain neurons fire enough, that i figured out the problem. My model column count can change and it does change, however, I had not written anything that notifies about column count changing beginRemoveColumns and endRemoveColumns and beginInsertColumns and endInsertColumns. I implemented those in my code like so
void RDMSensorModels::UpdateColumnCount() {
int sensorCount = 1;
for (auto device : m_RDMDevices) {
int deviceSensorCount = device->Sensors().size();
if (deviceSensorCount + 1 > sensorCount)
sensorCount = deviceSensorCount + 1; // +1 for device column
}
if (m_ColumnCount != sensorCount) {
if (m_ColumnCount < sensorCount) {
beginInsertColumns(QModelIndex(), m_ColumnCount, sensorCount - 1);
m_ColumnCount = sensorCount;
endInsertColumns();
} else {
beginRemoveColumns(QModelIndex(), sensorCount, m_ColumnCount - 1);
m_ColumnCount = sensorCount;
endRemoveColumns();
}
}
}
And the proxy model now works as expected. Hopefully this helps anyone else having issues with QSortFilterProxyModel.
It's interesting to note that the QAbstractItemModelTester did not catch this problem as I would have expected it to as my model changes column count depending on the largest sensor count for devices currently found.
I hame the following class that basically contains an std::vector<PassoProgramma>, and constitutes the model for a QTableView.
~ Programma.h ~
#ifndef PROGRAMMA_H
#define PROGRAMMA_H
#include <vector>
#include "PassoProgramma.h"
#include <QString>
#include <string>
#include <QAbstractTableModel>
#include <QFont>
class Programma : public QAbstractTableModel
{
Q_OBJECT
public:
Programma(QString, std::vector<PassoProgramma>, std::map<unsigned char, Asse>* const);
Programma(const std::string, std::map<unsigned char, Asse>* const);
void salva(const std::string, const std::string = "");
int sizeHintForRow() const ;
bool isIndexValid(const QModelIndex& index) const;
inline std::string getNome() const { return nome; };
inline std::vector<PassoProgramma>* const getPassi() { return &Passi; }
inline bool isSalvato() const { return salvato; }
int rowCount(const QModelIndex&) const override;
int columnCount(const QModelIndex&) const override;
QVariant data(const QModelIndex&, int) const override;
QVariant headerData(int, Qt::Orientation, int) const override;
bool dropMimeData(const QMimeData*, Qt::DropAction , int , int , const QModelIndex&) override;
bool removeRows(int, int, const QModelIndex&) override;
bool insertRows(int, int, const QModelIndex&) override;
bool moveRows(const QModelIndex&, const int, const int, const QModelIndex&, int) override;
QMimeData* mimeData(const QModelIndexList&) const override;
Qt::DropActions supportedDropActions() const override;
Qt::ItemFlags flags(const QModelIndex&) const override;
bool setData(const QModelIndex&, const QVariant&, int) override;
QStringList mimeTypes() const override;
PassoProgramma* operator[](int i)
{
if(i < Passi.size()) return &Passi.at(i);
else throw new std::exception();
};
static const QFont headerFont;
static const QFont dataFont;
private:
struct myLocale : std::numpunct<char>
{
protected :
char do_thousands_sep() const override { return '\0' ; }
char do_decimal_point() const override { return '.' ; }
std::string do_grouping() const override { return "" ; }
};
std::vector<PassoProgramma> Passi;
std::string nome;
bool salvato;
std::map<unsigned char, Asse>* const assi;
};
class ELoadException : public std::exception
{
private:
std::string message_;
public:
ELoadException(const std::string& message) : message_(message) {};
virtual const char* what() const throw()
{
return message_.c_str();
}
};
#endif
~ Programma.cpp ~
#include "Programma.h"
#include "Editor.h"
#include <iostream>
#include <fstream>
#include <sstream>
#include <cstring>
#include <regex>
#include <QSize>
#include <QMimeData>
#include <QDataStream>
const QFont Programma::headerFont("Open Sans", 17, QFont::Weight::DemiBold);
const QFont Programma::dataFont("Open Sans", 22, QFont::Weight::DemiBold);
inline std::string& ltrim(std::string& str, const std::string& chars = "\t\v\f ")
{
str.erase(0, str.find_first_not_of(chars));
return str;
}
inline std::string& rtrim(std::string& str, const std::string& chars = "\t\v\f ")
{
str.erase(str.find_last_not_of(chars) + 1);
return str;
}
inline std::string& trim(std::string& str, const std::string& chars = "\t\v\f ")
{
return ltrim(rtrim(str, chars), chars);
}
Programma::Programma(QString nome, std::vector<PassoProgramma> passi, std::map<unsigned char, Asse>* const assi) : Passi(passi), assi(assi)
{
this->nome = nome.toStdString();
}
Programma::Programma(std::string fileFullName, std::map<unsigned char, Asse>* const assi) : assi(assi)
{
std::ifstream stream;
try
{
stream.open(fileFullName, std::ios::in);
}
catch(std::exception& e)
{
std::stringstream st;
st << "Error opening file \"" << fileFullName << "\".\nError: " << e.what();
throw ELoadException(st.str());
}
int slash = fileFullName.find_last_of("/");
nome = fileFullName.substr(slash + 1);
Passi.reserve(100);
std::string line;
char* token;
unsigned int lineCount = 0, tokenCount;
while(std::getline(stream, line)) // Per ogni linea del file estraggo fino a 6 token
{
if(trim(line).length() == 0) continue;
// Controllo che la linea appena letta dal file contenga esattamente 2, 4 o 6 coppie <lettera, float>.
if(!std::regex_match(line, std::regex(R"(^(?:[A-Z] [-+]?[0-9]+(?:\.[0-9]+)?(?: |$)){1,3}$)")))
{
std::stringstream st;
st << "Line #" << lineCount << " of file \"" << fileFullName << "\" is invalid.";
throw ELoadException(st.str());
}
++lineCount;
PassoProgramma passo;
tokenCount = 0;
char* cstr = new char[line.length() + 1];
std::strcpy(cstr, line.c_str()); // Converto la stringa costante in char* non costante, per poterla fornire a strtok.
token = strtok(cstr, " ");
while(token)
{
++tokenCount;
switch(tokenCount)
{
case 1:
passo.asse1 = &assi->at(token[0]);
break;
case 2:
{
std::istringstream iStr(token);
iStr >> passo.target1;
break;
}
case 3:
passo.asse2 = &assi->at(token[0]);
break;
case 4:
{
std::istringstream iStr(token);
iStr >> passo.target2;
break;
}
case 5:
passo.asse3 = &assi->at(token[0]);
break;
case 6:
{
std::istringstream iStr(token);
iStr >> passo.target3;
break;
}
}
token = strtok(NULL, " "); // Vado al prossimo token senza cambiare la stringa da dividere
}
Passi.push_back(passo);
}
salvato = true;
}
void Programma::salva(const std::string path, const std::string nome /* facoltativo; se specificato costituir?? il nome del file */)
{
if(nome != "")
this->nome = nome;
std::stringstream destFileFullName("");
destFileFullName << path << "/" << this->nome << ".prg";
std::ofstream f;
f.imbue(std::locale(std::locale("en_US.UTF-8"), new Programma::myLocale()));
try
{
f.open(destFileFullName.str());
}
catch(std::exception& e)
{
std::stringstream st;
st << "Error writing to file \"" << st.str() << "\".\nError: " << e.what();
throw ELoadException(st.str());
}
for(const PassoProgramma& passo : Passi)
{
f << passo.toString() << std::endl;
}
// f.flush();
f.close();
salvato = true;
}
int Programma::rowCount(const QModelIndex& parent) const
{
Q_UNUSED(parent);
return Passi.size();
}
int Programma::columnCount(const QModelIndex& parent) const
{
Q_UNUSED(parent);
return 6;
}
QVariant Programma::data(const QModelIndex& index, int role) const
{
if(!index.isValid())
return QVariant();
if(role == Qt::TextAlignmentRole)
{
return QVariant(Qt::AlignHCenter | Qt::AlignVCenter);
}
else if(role == Qt::DisplayRole || role == Qt::EditRole)
{
switch(index.column())
{
case 0:
return QString(Passi.at(index.row()).asse1->asse);
break;
case 1:
return QString::number(Passi.at(index.row()).target1, 'f', Passi.at(index.row()).asse1->getCifreDecimali()).replace("-", Editor::MENO);
break;
case 2:
return (Passi.at(index.row()).getNumeroAssi() < 2) ? QVariant() : QString(Passi.at(index.row()).asse2->asse);
break;
case 3:
return (Passi.at(index.row()).getNumeroAssi() < 2) ? QVariant() : QString::number(Passi.at(index.row()).target2, 'f', Passi.at(index.row()).asse2->getCifreDecimali()).replace("-", Editor::MENO);
break;
case 4:
return (Passi.at(index.row()).getNumeroAssi() < 3) ? QVariant() : QString(Passi.at(index.row()).asse3->asse);
break;
case 5:
return (Passi.at(index.row()).getNumeroAssi() < 3) ? QVariant() : QString::number(Passi.at(index.row()).target3, 'f', Passi.at(index.row()).asse3->getCifreDecimali()).replace("-", Editor::MENO);
break;
}
}
else if(role == Qt::SizeHintRole)
{
switch(index.column())
{
case 0:
case 2:
case 4:
return QSize(50, 42);
case 1:
case 3:
case 5:
return QSize(105, 42);
}
}
else if(role == Qt::FontRole) return dataFont;
return QVariant();
}
QVariant Programma::headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const
{
if(Qt::Orientation::Horizontal == orientation)
{
if(role == Qt::DisplayRole)
{
switch(section)
{
case 0:
case 2:
case 4:
return QString("ASSE");
case 1:
case 3:
case 5:
return QString("TARGET");
}
}
else if(role == Qt::TextAlignmentRole)
{
return Qt::AlignCenter;
}
else if(role == Qt::FontRole) return headerFont;
else if(role == Qt::SizeHintRole)
{
switch(section)
{
case 0:
case 2:
case 4: return QSize(50, 28);
case 1:
case 3:
case 5: return QSize(105, 28);
}
}
}
else
{
if(role == Qt::DisplayRole)
{
return QString::number(section + 1);
}
else if(role == Qt::TextAlignmentRole)
{
return Qt::AlignRight;
}
/*
else if(role == Qt::SizeHintRole)
{
return QSize(45, 45);
}
*/
else if(role == Qt::FontRole) return headerFont;
}
return QVariant();
}
int Programma::sizeHintForRow() const
{
return 42;
}
bool Programma::dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent)
{
QByteArray encodedData = data->data("application/text");
QDataStream stream(&encodedData, QIODevice::ReadOnly);
QStringList newItems;
int rows = 0;
// I dati contengono una stringa multilinea: devo dividerli nelle varie linee.
while(!stream.atEnd())
{
QString text;
stream >> text;
newItems << text;
++rows;
}
// Divido ogni linea nei token di cui รจ composta.
for(int i = 0; i < rows; i++)
{
PassoProgramma nuovoPasso;
int tokenCount = 0;
std::string token(strtok(&(newItems[i].toStdString()[0]), " "));
while(token.length() > 0)
{
++tokenCount;
switch(tokenCount)
{
case 1:
nuovoPasso.asse1 = &assi->at(token[0]);;
break;
case 2:
{
nuovoPasso.target1 = std::stof(token);
break;
}
case 3:
nuovoPasso.asse2 = &assi->at(token[0]);
break;
case 4:
{
nuovoPasso.target2 = std::stof(token);
break;
}
case 5:
nuovoPasso.asse3 = &assi->at(token[0]);
break;
case 6:
{
nuovoPasso.target3 = std::stof(token);
break;
}
}
token = strtok(NULL, " "); // Va al prossimo token senza modificare la stringa da dividere
}
Passi.insert(Passi.begin() + row, nuovoPasso);
}
salvato = false;
return true;
}
bool Programma::removeRows(int row, int count, const QModelIndex& parent = QModelIndex())
{
if(row < 0 || row >= Passi.size() || count <= 0 || (row + count) > Passi.size())
{
return false;
}
beginRemoveRows(parent, row, row + count - 1);
Passi.erase(Passi.begin() + row, Passi.begin() + row + count);
endRemoveRows();
salvato = false;
return true;
}
bool Programma::insertRows(int row, int count, const QModelIndex& parent = QModelIndex())
{
if(row < 0 || count < 0 || row + count >= Passi.size())
{
return false;
}
beginInsertRows(parent, row, row + count - 1);
for(int i = 0; i < count; i++)
{
Passi.insert(Passi.begin() + row, PassoProgramma());
}
endInsertRows();
return true;
}
bool Programma::moveRows(const QModelIndex& sourceParent, const int sourceRow, const int count, const QModelIndex& destinationParent, int destinationChild)
{
for(int i = 0; i < count; ++i)
{
PassoProgramma toBeMoved = Passi.at(sourceRow + i);
Passi.insert(Passi.begin() + destinationChild + i, toBeMoved);
Passi.erase(Passi.begin() + sourceRow + i);
}
salvato = false;
return true;
}
QMimeData* Programma::mimeData(const QModelIndexList& indexes) const
{
QMimeData* mimeData = new QMimeData();
QByteArray encodedData;
QDataStream stream(&encodedData, QIODevice::WriteOnly);
foreach(const QModelIndex& index, indexes)
{
if(index.isValid())
{
stream << Passi.at(index.row()).toString().c_str() << '\n';
}
}
mimeData->setData("application/text", encodedData);
return mimeData;
}
Qt::DropActions Programma::supportedDropActions() const
{
return Qt::MoveAction;
}
Qt::ItemFlags Programma::flags(const QModelIndex& index) const
{
if(index.isValid())
{
if(index.column() < 2 && Passi[index.row()].getNumeroAssi() == 0
|| index.column() < 4 && Passi[index.row()].getNumeroAssi() == 1
|| Passi[index.row()].getNumeroAssi() >= 2
)
return QAbstractItemModel::flags(index) | Qt::ItemIsEditable | Qt::ItemIsEnabled;
}
return QAbstractItemModel::flags(index) & ~Qt::ItemIsEnabled;
}
bool Programma::setData(const QModelIndex& index, const QVariant& value, int role)
{
if(index.isValid() && role == Qt::EditRole)
{
switch(index.column())
{
case 0: Passi[index.row()].asse1 = &assi->at(value.toString()[0].toLatin1());
break;
case 1: Passi[index.row()].target1 = value.toString().replace(Editor::MENO, "-").toFloat();
break;
case 2: Passi[index.row()].asse2 = &assi->at(value.toString()[0].toLatin1());
break;
case 3: Passi[index.row()].target2 = value.toString().replace(Editor::MENO, "-").toFloat();
break;
case 4: Passi[index.row()].asse3 = &assi->at(value.toString()[0].toLatin1());
break;
case 5: Passi[index.row()].target3 = value.toString().replace(Editor::MENO, "-").toFloat();
break;
}
salvato = false;
emit dataChanged(index, index);
return true;
}
else return false;
}
bool Programma::isIndexValid(const QModelIndex& index) const
{
return index.row() >= 0 && index.row() < Passi.size() && index.isValid();
}
QStringList Programma::mimeTypes() const
{
return QStringList() << "application/text";
}
As you can see, I have overridden (and then implemented in Programma.cpp) all the basic events for moving, deleting and creating rows.
I have a button which deletes the current row and one that inserts a new row; furthermore, I wish to reorder the rows via drag&drop.
The aforementioned two buttons do work, but the drag&drop feature isn't working: the rows of the table do move, but none of the methods moveRows, mimeData and dropMimeData ever gets called. In consequence of this, the row header numbers follow the respective data; instead the headers should be renumebred automatically after each dragdrop.
Why does this happen?
I stitched to the examples provided in the Qt5 documentation.
Maybe my problem is trivial, but I'm developing in C++11 under Linux being a total newbie in both. (Only my boss knows why).
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 QTableView connected to my custom Model:
class QueueItem {
public:
enum QueueStatus { PENDING = 0, INPROGRESS, FINISHED, FAILED };
private:
QueueStatus status_;
QString filename_;
QString localPath_;
long long filesize_;
int progress_;
public:
QueueItem(const QString & file, const QString & localPath) :
filename_(file), localPath_(localPath)
{
filesize_ = 0;
progress_ = 0;
status_ = PENDING;
}
QString getFilename() const
{
return filename_;
}
QString getLocalPath() const
{
return localPath_;
}
long long getFilesize() const
{
return filesize_;
}
int getProgess() const
{
return progress_;
}
QueueStatus getStatus() const
{
return status_;
}
void setProgress(unsigned int prg){
progress_ = prg;
}
void setStatus(QueueItem::QueueStatus status){
status_ = status;
}
};
class QueueModel : public QAbstractTableModel {
private:
QList<QueueItem> data_;
public:
QueueModel(QObject * parent = 0) : QAbstractTableModel(parent) {}
int rowCount(const QModelIndex &) const { return data_.count(); }
int columnCount(const QModelIndex &) const { return 4; }
QVariant data(const QModelIndex &index, int role) const {
if (role != Qt::DisplayRole && role != Qt::EditRole) return QVariant();
const QueueItem & queueItem = data_[index.row()];
switch (index.column()) {
case 0: return queueItem.getFilename();
case 1: return queueItem.getFilesize() + " bytes";
case 2: return queueItem.getProgess() + "%";
case 3: {
std::string str;
switch (queueItem.getStatus()){
case QueueItem::PENDING :
str = "Pending";
break;
case QueueItem::INPROGRESS:
str = "In Progress";
break;
case QueueItem::FINISHED:
str = "Finished";
break;
case QueueItem::FAILED:
str = "Failed";
break;
default:
str = "Unkown";
}
return QString::fromStdString(str);
}
default: return QVariant();
};
}
QVariant headerData(int section, Qt::Orientation orientation, int role) const {
if (orientation != Qt::Horizontal) return QVariant();
if (role != Qt::DisplayRole) return QVariant();
switch (section) {
case 0: return "Filename";
case 1: return "Filesize";
case 2: return "Progress";
case 3: return "Status";
default: return QVariant();
}
}
void refresh(){
emit dataChanged(index(0, 0), index(data_.count()-1, 5));
}
void append(const QueueItem & queueItem) {
beginInsertRows(QModelIndex(), data_.count(), data_.count());
data_.append(queueItem);
endInsertRows();
}
};
That's how it's used:
// Queue Table
ui.tvQueue->setModel(&queueModel_);
ui.tvQueue->setItemDelegateForColumn(2,new ProgressBarDelegate(this));
So I created the following ItemDelegate:
class ProgressBarDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
ProgressBarDelegate::ProgressBarDelegate(QObject *parent = 0)
: QStyledItemDelegate(parent)
{
}
void ProgressBarDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
int progress = index.model()->data().toInt(); // How do I access my models .progress_ property?
QStyleOptionProgressBar progressBarOption;
progressBarOption.rect = option.rect;
progressBarOption.minimum = 0;
progressBarOption.maximum = 100;
progressBarOption.progress = progress;
progressBarOption.text = QString::number(progress) + "%";
progressBarOption.textVisible = true;
QApplication::style()->drawControl(QStyle::CE_ProgressBar,
&progressBarOption, painter);
}
};
The progressbar is correctly shown, but it is always at position 0. I expect the problem to be located in the last section at the line int progress = index.model()->data().toInt();
I'm not getting the correct progress_ value of the model, but always 0. How do I access my models progress_ property?
You can't convert a string containing other than numeric characters to the number. Your column 2 data contain a % character, so remove it before conversion:
int progress = index.model()->data().toString().replace("%", "").toInt();
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);
}