Qt: Browsing filesystem with QListView and QFileSystemModel. How to higlight first item in a folder?

anr78 picture anr78 · Aug 10, 2011 · Viewed 11.6k times · Source

I'm doing what the topic says on a system without keyboard/mouse, so I need to make this work "from code". When I change the RootIndex of the QListView I want to highlight the first row.

Here's mainwindow.cpp from a small testproject I've made:

#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QEvent>
#include <QKeyEvent>
#include <QDebug>
#include <QTimer>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    model = new QFileSystemModel;
    model->setRootPath("/Users/anders/Downloads/Browser");

    listView = new QListView;
    listView->setModel(model);
    listView->show();

    QTimer::singleShot(2000, this, SLOT(LightItUp1()));

}

void MainWindow::LightItUp1()
{
    qDebug("LightItUp1");
    listView->setRootIndex(model->index("/Users/anders/Downloads"));
    listView->setCurrentIndex(model->index(0, 0, listView->rootIndex()));

    QTimer::singleShot(2000, this, SLOT(LightItUp2()));
}

void MainWindow::LightItUp2()
{
    qDebug("LightItUp2");
    listView->setRootIndex(model->index("/Users/anders/Downloads/Browser"));
    listView->setCurrentIndex(model->index(0, 0, listView->rootIndex()));

    QTimer::singleShot(2000, this, SLOT(LightItUp3()));
}


void MainWindow::LightItUp3()
{
    qDebug("LightItUp3");
    listView->setRootIndex(model->index("/Users/anders/Downloads"));
    listView->setCurrentIndex(model->index(0, 0, listView->rootIndex()));

    QTimer::singleShot(2000, this, SLOT(LightItUp4()));
}


void MainWindow::LightItUp4()
{
    QString p = "/Users/anders/Downloads/Mail";
    listView->setRootIndex(model->index(p));
    listView->setCurrentIndex(model->index(0, 0, listView->rootIndex()));
}

MainWindow::~MainWindow()
{
    delete listView;
    delete model;
    delete ui;
}

In this example LightItUp 1-3 do what I want, but LightItUp4 does not. If I swap the folders in 2 & 4 both of them fail to do what I want, while 1 & 3 still work. I suspect I have misunderstood something about how to use this Model/View, but have no idea what.

Edit: created a simpler example with the error checking @buck mentioned. See the comments in the source code.

const QString rp = "/home/anders/src/";

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    model = new QFileSystemModel;
    model->setRootPath(rp); //using model->setRootPath(rp + "/trunk") instead works

    listView = new QListView;
    listView->setModel(model);
    listView->show();

    QTimer::singleShot(2000, this, SLOT(LightItUp1()));

}

void MainWindow::LightItUp1()
{
    qDebug("LightItUp1");
    QModelIndex p = model->index(rp + "/trunk");
    if (!p.isValid()) {
        qDebug("index not valid\n");
        return;
    }

    //model->setRootPath(rp + "/trunk") here does not make it work
    listView->setRootIndex(p);
    listView->setCurrentIndex(model->index(0, 0, p));
}

I thought that when I do setRootPath(rp) on the model, and then set the view to use the model, the view should able to move around in all subfolders of rp if I set the indexes correctly. I'll reread the Qtdocs on Model/View, QListView and QFileSystemModel, but wanted to post this in case someone understands what is happening.

Answer

buck picture buck · Aug 11, 2011

I had some help from here and these are my conclusions:

In order for the QFileSystemModel to work properly, the GUI event loop needs to be running. I'm guessing you added the QTimer::singleShot(...) line because of this? However, you only gave it 2 seconds. From the documentation for QFileSystemModel:

Calls to rowCount() will return 0 until the model populates a directory.

This means after your MainWindow is constructed, you have 2 seconds for everything else to be constructed, the GUI event loop to start, and then for the QFileSystemModel to populate the directory. Are the directories where this is failing large? I am guessing so.

What you could try would be to give the timer a longer interval. A better solution may be to create a shortcut that selects the first thing in the list, like this:

QShortcut* sh = new QShortcut(QKeySequence("Ctrl+1"), this);
connect(sh, SIGNAL(activated()), this, SLOT(LightUpFirst()));

and the LightUpFirst function does the selecting. Hope that helps!