QTreeView not displaying values



  • Hello,
    I'm working with a TreeView with my own implementation of the QAbstractItemModel. It's the first time that I'm working with it and I don't really understand what i'm doing wrong. I have 2 problems with my implementation:

    • There is no data in the rows of the TreeView
    • When I expand a node it crashes because of some recursive error that I don't understand

    TreeView when running
    This is the TreeView in my application, there should be text in each of the lines, I have debugged it and the data method from the view model returns text so as far as I know there should be stuff there.

    The error is a stack overflow from the QDataArray, this happens when you press the little expansion arrow in the image above.
    Stack overflow error screenshot

    What am I doing wrong?

    H File:

    class ImageSetViewModel : public QAbstractItemModel
    {
    	struct Set;
    
    	struct Image
    	{
    		int cameraId;
    		QString fileName;
    
    		Set* parent;
    	};
    
    	struct Set
    	{
    		int setId;
    		int row;
    		QString name;
    		QList<Image*>* images = new QList<Image*>();
    	};
    
    	Q_OBJECT
    public:
    	ImageSetViewModel();
    	~ImageSetViewModel();
    
    	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;
    	QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
    	Qt::ItemFlags flags(const QModelIndex &index) const override;
    	QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
    	QModelIndex parent(const QModelIndex &index) const override;
    
    	void clear();
    	bool addSet(int, std::string, std::string);
    
    private:
    	bool setExists(int id);
    
    	QList<Set*>* displayData = new QList<Set*>();
    };
    

    Cpp file:

    ImageSetViewModel::ImageSetViewModel()
    {
    }
    
    
    ImageSetViewModel::~ImageSetViewModel()
    {
    }
    
    int ImageSetViewModel::rowCount(const QModelIndex& parent) const
    {
    	if (parent.column() > 0)
    		return 0;
    
    	//amount of items in set
    	if (parent.row() >= 0)
    		return displayData->at(parent.row())->images->size();
    
    	return displayData->size();
    }
    
    int ImageSetViewModel::columnCount(const QModelIndex& parent) const
    {
    	return 2;
    }
    
    QVariant ImageSetViewModel::data(const QModelIndex& index, int role) const
    {
    	if (role == Qt::DisplayRole)
    	{
    		if (typeid(index.internalPointer()) == typeid(Set*))
    		{
    			Set* instance = static_cast<Set*>(index.internalPointer());
    
    			switch (index.column())
    			{
    			case 0:
    				QVariant(instance->images->at(index.row())->fileName);
    			case 1:
    				QVariant(instance->images->at(index.row())->cameraId);
    			default:
    				return QVariant();
    			}
    		}
    
    		switch (index.column())
    		{
    		case 0:
    			QVariant(displayData->at(index.row())->name);
    		case 1:
    			QVariant(displayData->at(index.row())->setId);
    		default:
    			return QVariant();
    		}
    	}
    
    	return QVariant();
    }
    
    QVariant ImageSetViewModel::headerData(int section, Qt::Orientation orientation, int role) const
    {
    	if (role != Qt::DisplayRole) return QVariant();
    	if (orientation != Qt::Horizontal) return QVariant();
    
    	if (section == 1) return "Name";
    	else return "ID";
    }
    
    Qt::ItemFlags ImageSetViewModel::flags(const QModelIndex& index) const
    {
    	return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
    }
    
    QModelIndex ImageSetViewModel::index(int row, int column, const QModelIndex& parent) const
    {
    	if (!hasIndex(row, column, parent))
    		return QModelIndex();
    
    	if (typeid(parent.internalPointer()) == typeid(Set*))
    	{
    		Set* instance = static_cast<Set*>(parent.internalPointer());
    		createIndex(row, column, instance->images->at(row));
    	}
    
    	return createIndex(row, column, displayData->at(row));
    }
    
    QModelIndex ImageSetViewModel::parent(const QModelIndex& index) const
    {
    	if (!index.isValid())
    		return QModelIndex();
    
    	//return image parent (a Set)
    	if (typeid(index.internalPointer()) == typeid(Image*))
    	{
    		Set* instance = static_cast<Image*>(index.internalPointer())->parent;
    		createIndex(instance->row, 0, instance);
    	}
    
    	return QModelIndex();
    }
    
    void ImageSetViewModel::clear()
    {
    	beginResetModel();
    	displayData->clear();
    	endResetModel();
    }
    
    bool ImageSetViewModel::addSet(int id, std::string name, std::string imageJson)
    {
    	if (setExists(id)) return false;
    
    	int rows = rowCount();
    
    	Set* set = new Set();
    	set->setId = id;
    	set->name = QString::fromStdString(name);
    	set->row = rows;
    
    	nlohmann::json data = nlohmann::json::parse(imageJson.c_str());
    	for (int i = 0; i < data.size(); ++i)
    	{
    		Image* img = new Image();
    		img->fileName = QString::fromStdString(data[i]["path"]);
    		img->cameraId = data[i]["id"];
    
    		set->images->append(img);
    	}
    
    	beginInsertRows(QModelIndex(), rows, rows);
    	displayData->append(set);
    	endInsertRows();
    
    	return true;
    }
    
    bool ImageSetViewModel::setExists(int id)
    {
    	for (int i = displayData->size(); i < 0; --i)
    		if (displayData->at(i)->setId == id) return true;
    
    	return false;
    }
    


  • Introductory remarks:

    • In most (>80%) cases you do not need a custom model, QStandardItemModel works perfectly
    • It's always a good Idea to run custom model through the Model Test

    Now:

    • ImageSetViewModel::rowCount(const QModelIndex& parent) and ImageSetViewModel::columnCount should check parent.isValid()
    • this
    			switch (index.column())
    			{
    			case 0:
    				QVariant(instance->images->at(index.row())->fileName);
    			case 1:
    				QVariant(instance->images->at(index.row())->cameraId);
    			default:
    				return QVariant();
    			}
    

    and this

    		switch (index.column())
    		{
    		case 0:
    			QVariant(displayData->at(index.row())->name);
    		case 1:
    			QVariant(displayData->at(index.row())->setId);
    		default:
    			return QVariant();
    		}
    

    are always equivalent to return QVariant(); you forgot a couple of return



  • Yes the return thing solves why there were no entries in the fields. Oh I feel so stupid :(

    I wish that the model test that you linked was in more places where people ask questions about QAbstractItemModels that would be super handy.

    Are there any common things that people do which could lead to the recursive error that I'm also having?



  • @The-newest-user said in QTreeView not displaying values:

    Are there any common things that people do which could lead to the recursive error that I'm also having?

    A lot. and I mean a lot.
    Since you don't reimplement hasChildren() the rowCount/columnCount not checking for the parent validity might be a problem but it's just a guess.
    Stack trace and a lot of red bull are your friends :)


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.