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

11,718

Solution 1

I think I have it working now. After changing the rootIndex of the list, I have to wait for the model to do its work. I don't set the currentIndex in the new directory until I get the directoryLoaded signal from the model. Now highlighting works. The data from the model is not sorted, so row=0 & col=0 is not the first item in the list after all, but that's another topic :)

Edit: fiddled a bit more with this tonight, and added the final touches.

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);

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

    connect(model,
            SIGNAL(directoryLoaded(QString)),
            this,
            SLOT(model_directoryLoaded(QString)));

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

void MainWindow::model_directoryLoaded(QString path)
{
    qDebug() << "loaded" << path;
    model->sort(0, Qt::AscendingOrder);
    list->setCurrentIndex(model->index(0, 0, list->rootIndex()));
}

void MainWindow::changeRoot()
{
    qDebug() << "changeRoot";
    model->setRootPath(rp + "/trunk");
    list->setRootIndex(model->index(rp + "/trunk"));
}

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

Solution 2

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!

Share:
11,718
anr78
Author by

anr78

Updated on June 21, 2022

Comments

  • anr78
    anr78 about 2 years

    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.

  • anr78
    anr78 almost 13 years
    I've read that post as well, and it is indeed the reason for the timer. Actually the directories are not very large, and even if I increase it to 20 seconds it fails.
  • anr78
    anr78 almost 13 years
    In my real code the time from the model starts to populate until I try the operate on the listView can be several minutes, but it still fails in the same way.
  • buck
    buck almost 13 years
    What are the contents of the directory where this is failing? Is it all directories or all files or mixed (maybe this plays a role??)? Also, it could be that the directory where this is failing is not loaded by the QFileSystemModel. Maybe when you change the root index of the view you should also call setRootPath
  • anr78
    anr78 almost 13 years
    The contents of the dir does not seem to matter. I tried setting rootPath (see the comment in my example) without any luck, but I suspect I should wait for some signals from QFileSystemModel before acting. When I wait for directoryLoaded(QString) I manage to highlight something, thought it's not the first directory. But hey, its progress.
  • ransh
    ransh over 9 years
    Hi, Did you manage to highlight specific file ? Is the code posted here the final results ? How do you scroll down and up between files (and highlight each time the selected file) Thanks, Ran
  • anr78
    anr78 over 9 years
    I do not remember how this ended. It was a long time ago, and the code does not exist anymore (and never reached a VCS). Sorry.
  • ransh
    ransh over 9 years
    Thanks, so you do not recommend to use this code, I understand, Right ?
  • anr78
    anr78 over 9 years
    If it works it's usable. I honestly don't remember :)