Grouping items in QTreeView using a separator



  • I am loading a list of strings into QTreeView and I want to group them into virtual folders like so:

    text
     |_ english
           |_ game
                |_ pro_scen.msg
                |_ pro_quests.msg
                |_ ...
    

    However, the code below results in all of the nested items being placed into the "example" folder instead of "text". Can you spot what I am doing wrong?

    QStandardItemModel *model = new QStandardItemModel();
    
    QVector<QString> entries({
        {"text/english/game/pro_scen.msg"},
        {"text/english/game/pro_wall.msg"},
        {"text/english/game/proto.msg"},
        {"text/english/game/quests.msg"},
        {"example/english/game/pro_scen.msg"},
        {"script.int"}
    });
    
    model->setColumnCount(1);
    
    for(auto &entry : entries)
    {
        QStringList fileParts = entry.split("/");
    
        QStandardItem *root;
        QStandardItem *child;
    
        for(int i = 0; i < fileParts.size(); i++)
        {
            child = new QStandardItem(fileParts[i]);
    
            // FIXME: this will exclude items with the same name that are in different directories
            QList<QStandardItem*> exists = model->findItems(fileParts[i], Qt::MatchExactly | Qt::MatchRecursive);
    
            if(exists.empty())
            {
                // item does not exist
                if(i == 0)
                {
                    // root item
                    root = child;
                    model->appendRow(root);
                } else {
                    // child of some other item
                    root->appendRow(child);
                    root = child;
                }
            }
            else
            {
                // item is already there
                root = exists[0];
            }
        }
    }
    
    ui->treeView->setModel(model);


  • void appendPath(QAbstractItemModel* const model, QStringList& fileParts, const QModelIndex& parent = QModelIndex()){
    	Q_ASSERT(model);
    	if(fileParts.isEmpty()) 
    		return;
    	if(model->columnCount(parent)<=0)
    		model->insertColumn(0,parent);
    	QModelIndex currIdx;
    	const QString currLevel = fileParts.takeFirst();
    	const int rowCount = model->rowCount(parent);
    	for(int i=0;i<rowCount;++i){
    		if(model->index(i,0,parent).data().toString().compare(currLevel)==0){
    			currIdx = model->index(i,0,parent);
    			break;
    		}
    	}
    	if(!currIdx.isValid()){
    		model->insertRow(rowCount,parent);
    		currIdx=model->index(rowCount,0,parent);
    		model->setData(currIdx,currLevel);
    	}
    	appendPath(model,fileParts,currIdx);
    }
    

    then you can use it:

    QStandardItemModel *model = new QStandardItemModel(this); //give a parent or you'll leak
    
    QVector<QString> entries({
        {"text/english/game/pro_scen.msg"},
        {"text/english/game/pro_wall.msg"},
        {"text/english/game/proto.msg"},
        {"text/english/game/quests.msg"},
        {"example/english/game/pro_scen.msg"},
        {"script.int"}
    });
    for(const QString& entry : entries)
    {
        QStringList fileParts = entry.split("/");
        appendPath(model,fileParts);
    }
    ui->treeView->setModel(model);
    


  • Perfect. Thank you.


Log in to reply
 

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