5

I am try an example in Model/View Programming.

http://doc.qt.io/qt-5/model-view-programming.html

To demonstrate how data can be retrieved from a model, using model indexes, we set up a QFileSystemModel without a view and display the names of files and directories in a widget. Although this does not show a normal way of using a model, it demonstrates the conventions used by models when dealing with model indexes.

We construct a file system model in the following way:

QFileSystemModel *model = new QFileSystemModel;
QModelIndex parentIndex = model->index(QDir::currentPath());
int numRows = model->rowCount(parentIndex);

In this case, we set up a default QFileSystemModel, obtain a parent index using a specific implementation of index() provided by that model, and we count the number of rows in the model using the rowCount() function.

This is my code:

QFileSystemModel* model = new QFileSystemModel;
QModelIndex parentIndex = model->index(QDir::currentPath());
qDebug() << QDir::currentPath();
// "/media/Local Data/Files/Programming/C++/build-DemostrateQModelIndex-Desktop_Qt_5_5_1_GCC_64bit-Debug"
qDebug() << "RowCount is " << model->rowCount(parentIndex);

But RowCount is always 0.

In the "build-DemostrateQModelIndex-Desktop_Qt_5_5_1_GCC_64bit-Debug" folder, there is files and folder inside. I expect row count should be the number of items inside.

I also tried initialized the QFileSystemModel;

QFileSystemModel* model = new QFileSystemModel;
model->setRootPath(QDir::rootPath());
QModelIndex parentIndex = model->index(QDir::currentPath());
qDebug() << "RowCount is " << model->rowCount(parentIndex);

RowCount is still 0.

Update 1: Applying the suggestion from Johannes Schaub. I add an QEventLoop to my code.

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QFileSystemModel* model = new QFileSystemModel;
    model->setRootPath(QDir::rootPath());
    QModelIndex parentIndex = model->index(QDir::currentPath());
    qDebug() << QDir::currentPath();
    // "/media/Local Data/Files/Programming/C++/build-DemostrateQModelIndex-Desktop_Qt_5_5_1_GCC_64bit-Debug"
    qDebug() << "First RowCount Call is " << model->rowCount(parentIndex);

    QEventLoop loop;
    QObject::connect(model, &QFileSystemModel::directoryLoaded, &loop, &QEventLoop::quit);
    loop.exec();
    qDebug() << "RowCount Call after eventloop is  " << model->rowCount(parentIndex);

    return a.exec();
}

I still get a row count of 0.

tom
  • 1,302
  • 1
  • 16
  • 30
  • Using `model->setRootPath(QDir::currentPath())` in your last sample (and not `QDir::rootPath()`) yields non-zero rows count after the event loop. – AntonK Mar 26 '19 at 22:37

3 Answers3

3

QFileSystemModel utilizes lazy and deferred loading. You need to watch on its signals, which will be emitted constantly until the entire directory has been loaded.

In particular, the docs say

Unlike QDirModel, QFileSystemModel uses a separate thread to populate itself so it will not cause the main thread to hang as the file system is being queried. Calls to rowCount() will return 0 until the model populates a directory.

In your case, you could probably run a local QEventLoop and connect the respective signals (directoryLoaded) of the model with the quit() slot of the event loop to wait for the population. I am unsure whether canFetchMore and fetchMore can be used for this scenario aswell to block on waiting for the population (afaik its main use is lazy loading when the user scrolls down in an infinite list, like for example a facebook pinwall stream). It's worth an attempt, at least.

@Kuba notes correctly that a local event loop is not intrinsically required. If you can afford leaving the context in which you create the QFileSystemModel (by storing it as a pointer member for example), and acting on the slot as a normal member function.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • "run a local QEventLoop" That's completely unnecessary. The main event loop will do that for you already. – Kuba hasn't forgotten Monica Nov 05 '15 at 16:37
  • @KubaOber we're talking about different things. In his code, he operates synchronously. What you're proposing is an asynchronous approach which returns to the main event loop, storing the model pointer away in some member and handling the directoryLoaded signal in a connected slot. But to keep his synchronous operations, you have to wait on a local event loop to keep things running and to keep being able to process the signal. – Johannes Schaub - litb Nov 05 '15 at 16:44
  • I haven't done much with Qt5 yet, but I believe you can pass lambdas as slots. So that would be a good approach aswell, if it works for him (granted, local event loops are to be avoided for their potential to stop an application from quitting and for their reentrancy issues...). – Johannes Schaub - litb Nov 05 '15 at 16:49
  • I am new to Qt. I don't hundred percent understand what you mean. What I get is: `QFileSystemModel` is not loaded immediately.So we need `QFileSystemModel` to emit a signal when it completed loading. Then, we start a `QEventLoop` to pause the main program. The `QEventLoop` will then grab the signal by the `quit()` slot. Finally, the main program continues. Am I right? – tom Nov 06 '15 at 07:05
1

The principle to use is:

  • Create the model and set its root path. At this stage you can assum the model is still empty or very few data have been loaded.
  • Let the model load data in its separate internal thread. Connect the directoryLoaded signal to a slot. When the model will have loaded the root path, the signal will be sent.
  • In the slot, check if the folder you are interested in is fully loaded. It won't at first. The reason is the model loads using lazy methods, and likely only the root path folder will be ready.
  • If your folder is not fully ready, asks the model to load it. This is done by using model.canFetchMore and model.fetchMore with the index of the folder of interest, and returning immediately (or you could try to work with folder entries already ready, but this alternative requires to manage the progress of the model readiness).
  • When the slot is ready (likely just after calling model.fetchMore, the test canFetchMore will return False, meaning the folder you're interested in is fully loaded and model.rowCount will now return the correct value. If you're interested in another folder, use model.canFetchMore and model.fetchMore again with the index of the new folder.

Instead of using model.canFetchMore, you may also compare the path of the folder of interest with the argument passed to the slot. This string indicates the path the signal was sent for.


You indicated you're using C++, I don't have a code in this language, however the following code in Python can be easily translated (self = this, and indented lines are equivalent to a pair of brackets in delimiting a block)


class MyWidget(QPlainTextEdit):

    def __init__(self):

        # Init superclass
        super(MyWidget, self).__init__()
        self.setReadOnly(True)
        self.show()

        # FS model, set root path
        self.model = QFileSystemModel()
        path = "C:/"
        self.model.setRootPath(path)

        # Perform next tasks after model root path is loaded
        self.model.directoryLoaded.connect(self.on_loaded)

    def on_loaded(self, loaded_path):
        # Folder to list
        folder_path = "C:/Users"  # <--- we are interested in this folder,
                                  #      not the root folder
        folder_index = self.model.index(folder_path)

        # Check the folder we are interested in is completely loaded
        if self.model.canFetchMore(folder_index):
            self.model.fetchMore(folder_index)
            return

        # Folder is now loaded, list children
        num_rows = self.model.rowCount(folder_index)
        for row in range(num_rows):
            # Child data
            num_columns = self.model.columnCount(folder_index)
            if num_columns > 0:
                # Child name in first column
                index = self.model.index(row, 0, folder_index)
                text += index.data(Qt.DisplayRole)

                # etc
mins
  • 6,478
  • 12
  • 56
  • 75
-2

int rowCount = ui->tableView->verticalHeader()->count();

  • It isn't at all clear what this answer is trying to say. An answer requires some explanation. In any event, this answer seems misguided: OP uses `QFileSystemModel` which does not have a `tableView` member. – ad absurdum Oct 16 '22 at 16:53