QFileSystemModel rowCount не работает должным образом

Я пробую пример в программировании модели/представления.

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

Чтобы продемонстрировать, как данные могут быть извлечены из модели, используя модельные индексы, мы настраиваем QFileSystemModel без представления и отображаем имена файлов и каталогов в виджете. Хотя это не показывает обычный способ использования модели, оно демонстрирует соглашения, используемые моделями при работе с модельными индексами.

Мы строим модель файловой системы следующим образом:

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

В этом случае мы устанавливаем QFileSystemModel по умолчанию, получаем родительский индекс, используя конкретную реализацию index(), предоставленную этой моделью, и мы подсчитываем количество строк в модели, используя функцию rowCount().

Это мой код:

QFileSystemModel* model = new QFileSystemModel;
QModelIndex parentIndex = model->index(QDir::currentPath());
qDebug() << QDir::currentPath();
// "/media/Local Data/Files/Programming/C++/build-DemostrateQModelIndex-Desktop_Qt
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();
}
5_1_GCC_64bit-Debug" qDebug() << "RowCount is " << model->rowCount(parentIndex);

Но RowCount всегда равен 0.

В папке «build-DemostrateQModelIndex-Desktop_Qt

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();
}
5
QFileSystemModel *model = new QFileSystemModel;
QModelIndex parentIndex = model->index(QDir::currentPath());
int numRows = model->rowCount(parentIndex);
GCC_64bit-Debug» есть файлы и папка внутри. Я ожидаю, что количество строк должно быть количеством элементов внутри.

Я также попытался инициализировать QFileSystemModel;

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

RowCount по-прежнему равен 0.

Обновление 1: применение предложения Йоханнеса Шауба. Я добавляю QEventLoop в свой код.

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

Я все еще получаю количество строк 0.


person tom    schedule 05.11.2015    source источник
comment
Использование model->setRootPath(QDir::currentPath()) в вашем последнем примере (а не QDir::rootPath()) дает ненулевое количество строк после цикла обработки событий.   -  person AntonK    schedule 27.03.2019


Ответы (2)


QFileSystemModel использует ленивую и отложенную загрузку. Вам нужно следить за его сигналами, которые будут излучаться постоянно, пока не будет загружен весь каталог.

В частности, в документах говорится

В отличие от QDirModel, QFileSystemModel использует отдельный поток для заполнения самого себя, поэтому основной поток не зависает при запросе файловой системы. Вызовы rowCount() будут возвращать 0, пока модель не заполнит каталог.

В вашем случае вы, вероятно, могли бы запустить локальный QEventLoop и соединить соответствующие сигналы (directoryLoaded) модели со слотом quit() цикла событий, чтобы дождаться заполнения. Я не уверен, можно ли использовать canFetchMore и fetchMore для этого сценария, а также для блокировки ожидания населения (на самом деле его основное использование - ленивая загрузка, когда пользователь прокручивает бесконечный список, например, поток пинволла facebook). По крайней мере, стоит попытаться.

@Kuba правильно отмечает, что локальный цикл событий по сути не требуется. Если вы можете позволить себе выйти из контекста, в котором вы создаете QFileSystemModel (например, сохранив его как элемент-указатель) и действовать в слоте как в обычной функции-члене.

person Johannes Schaub - litb    schedule 05.11.2015
comment
запустить локальный QEventLoop Это совершенно не нужно. Основной цикл событий уже сделает это за вас. - person Kuba hasn't forgotten Monica; 05.11.2015
comment
@KubaOber, мы говорим о разных вещах. В своем коде он работает синхронно. То, что вы предлагаете, представляет собой асинхронный подход, который возвращается к основному циклу обработки событий, сохраняя указатель модели в каком-либо элементе и обрабатывая сигнал directoryLoaded в подключенном слоте. Но чтобы поддерживать его синхронные операции, вам нужно ждать в локальном цикле событий, чтобы все работало и чтобы была возможность обрабатывать сигнал. - person Johannes Schaub - litb; 05.11.2015
comment
Я еще мало что сделал с Qt5, но я считаю, что вы можете передавать лямбда-выражения как слоты. Так что это тоже был бы хороший подход, если он сработает для него (конечно, следует избегать локальных циклов событий из-за их способности остановить приложение от выхода и из-за проблем с повторным входом...). - person Johannes Schaub - litb; 05.11.2015
comment
Я новичок в Qt. Я не понимаю на сто процентов, что вы имеете в виду. Я получаю следующее: QFileSystemModel не загружается сразу. Поэтому нам нужно, чтобы QFileSystemModel издавал сигнал после завершения загрузки. Затем мы запускаем QEventLoop, чтобы приостановить основную программу. Затем QEventLoop захватит сигнал слотом quit(). Наконец, основная программа продолжается. Я прав? - person tom; 06.11.2015

Принцип использования таков:

  • Создайте модель и установите ее корневой путь. На этом этапе вы можете предположить, что модель все еще пуста или загружено очень мало данных.
  • Позвольте модели загружать данные в отдельный внутренний поток. Подключите сигнал directoryLoaded к слоту. Когда модель загрузит корневой путь, будет отправлен сигнал.
  • В слоте проверьте, полностью ли загружена интересующая вас папка. Сначала не будет. Причина в том, что модель загружается ленивыми методами, и, скорее всего, будет готова только корневая папка.
  • Если ваша папка не полностью готова, просит модель загрузить ее. Это делается с помощью model.canFetchMore и model.fetchMore с индексом интересующей папки и немедленного возврата (или вы можете попробовать работать с уже готовыми записями папки, но этот вариант требует управления ходом готовности модели).
  • Когда слот будет готов (вероятно, сразу после вызова model.fetchMore, тест canFetchMore вернет False, что означает, что интересующая вас папка полностью загружена, а model.rowCount теперь вернет правильное значение. Если вас интересует другая папка, используйте model.canFetchMore и снова model.fetchMore с индексом новой папки.

Вместо использования model.canFetchMore вы также можете сравнить путь к интересующей папке с аргументом, переданным слоту. Эта строка указывает путь, по которому был отправлен сигнал.


Вы указали, что используете C++, у меня нет кода на этом языке, однако следующий код на Python можно легко перевести (self = this, а строки с отступом эквивалентны паре скобок в разграничении блока)


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
person mins    schedule 26.02.2019