QListView с миллионами элементов медленно работает с клавиатурой

Я использую QListView с пользовательской моделью, полученной из QAbstractItemModel. У меня порядка миллиона предметов. Я вызвал listView->setUniformItemSizes(true), чтобы предотвратить вызов логики компоновки при добавлении элементов в модель. Пока все работает так, как ожидалось.

Проблема в том, что использование клавиатуры для навигации по списку происходит медленно. Если я выбираю элемент в списке, а затем нажимаю вверх/вниз, выбор перемещается быстро, пока выбор не требует прокрутки списка. Затем он становится очень медленным. Нажатие page-up или page-down тоже очень тормозит. Кажется, проблема заключается в том, что элемент выбран (он же «текущий элемент») с помощью клавиатуры, а список также прокручивается вверх/вниз.

Если я использую мышь, навигация по списку выполняется быстро. Я могу использовать колесо мыши, которое быстро. Я могу перетаскивать полосу прокрутки вверх/вниз так быстро, как захочу — от верхней части списка к нижней — и вид списка обновляется ужасно быстро.

Любые идеи о том, почему комбинация изменения выбора и прокрутки списка так медленна? Есть ли жизнеспособный обходной путь?

Обновление от 09.09.15

Чтобы лучше проиллюстрировать проблему, я предоставляю дополнительную информацию в этом обновлении.

Проблемы с производительностью при использовании KEYBOARD + SCROLLING

В основном это вопрос производительности, хотя он в некоторой степени связан с пользовательским опытом (UX). Посмотрите, что происходит, когда я использую клавиатуру для прокрутки QListView:

Проблема с медленной прокруткой

Заметили замедление в нижней части страницы? Это главный вопрос моего вопроса. Позвольте мне объяснить, как я перемещаюсь по списку.

Пояснение:

  1. Начиная сверху, выбирается первый элемент в списке.
  2. Нажав и удерживая клавишу со стрелкой вниз, текущий элемент (выбор) изменяется на следующий элемент.
  3. Изменение выбора происходит быстро для всех элементов, которые в данный момент находятся в поле зрения.
  4. Как только список должен вывести следующий элемент в поле зрения, скорость выбора значительно замедляется.

Я ожидаю, что список должен иметь возможность прокручиваться со скоростью набора текста на моей клавиатуре — другими словами, время, необходимое для выбора следующего элемента, не должно замедляться при прокрутке списка.

Быстрая прокрутка с помощью МЫШИ

Вот как это выглядит, когда я использую мышь:

Быстрая навигация с помощью мыши

Пояснение:

  1. С помощью мыши я выбираю ручку полосы прокрутки.
  2. Быстро перетаскивая ручку полосы прокрутки вверх и вниз, список прокручивается соответственно.
  3. Все движения очень быстрые.
  4. Обратите внимание, что выборы не выполняются.

Это доказывает два основных момента:

  1. Проблема не в модели. Как видите, у модели нет проблем с производительностью. Он может доставлять элементы быстрее, чем они могут отображаться.

  2. При выборе И прокрутке производительность снижается. "Идеальный шторм" выбора и прокрутки (как показано с помощью клавиатуры для навигации по списку) вызывает замедление. В результате я предполагаю, что Qt каким-то образом выполняет большую обработку, когда выбор делается во время прокрутки, что обычно не выполняется.

Реализация не на Qt выполняется БЫСТРО

Я хочу отметить, что моя проблема, похоже, специфична для Qt.

Я уже реализовал этот тип вещей, прежде чем использовать другую структуру. То, что я пытаюсь сделать, находится в рамках теории представления модели. Я могу делать именно то, что описываю, на молниеносной скорости, используя juce ::ListBoxModel с juce::ListBox< /а>. Это глупо быстро (плюс, нет необходимости создавать повторяющийся индекс, такой как QModelIndex, для каждого отдельного элемента, когда каждый элемент уже имеет уникальный индекс). Я понимаю, что Qt нуждается в QModelIndex для каждого элемента в своей архитектуре представления модели, и хотя мне не нравятся накладные расходы, я думаю, что понял рациональность и могу с этим смириться. В любом случае, я не подозреваю, что именно эти QModelIndexes вызывают снижение производительности.

С реализацией JUCE я могу даже использовать клавиши page-up и page-down для навигации по списку, и он просто проносится по списку. Используя реализацию Qt QListView, он работает с пыхтением и лагает, даже с релизной сборкой.

Реализация модели-представления с использованием JUCE framework выполняется очень быстро. Почему реализация Qt QListView такая плохая?!

Мотивирующий пример

Трудно представить, зачем вам нужно так много элементов в представлении списка? Что ж, мы все уже видели подобное:

Индекс Visual Studio

Это индекс средства просмотра справки Visual Studio. Я не считал все предметы, но думаю, мы согласимся, что их очень много! Конечно, чтобы сделать этот список «полезным», они добавили поле фильтра, которое сужает то, что находится в представлении списка, в соответствии с входной строкой. Здесь нет никаких хитростей. Все это практичные, реальные вещи, которые мы все десятилетиями видели в настольных приложениях.

Но существуют ли миллионы элементов? Я не уверен, что это имеет значение. Даже если бы было «всего» 150 000 элементов (что является примерно точным на основе некоторых грубых измерений), легко указать, что вам нужно что-то сделать, чтобы сделать его пригодным для использования — и это то, что фильтр сделает для вас.

В моем конкретном примере используется список немецких слов в виде обычного текстового файла с чуть более чем 1,7 миллионами записей (включая изменчивые формы). Это, вероятно, только частичная (но все же значительная) выборка слов из немецкого текстового корпуса, который использовался составить этот список. Для лингвистических исследований это разумный вариант использования.

Забота об улучшении UX (пользовательского опыта) или фильтрации — отличные цели дизайна, но они выходят за рамки этого вопроса (я обязательно обращусь к ним позже в проекте).

Код

Хотите пример кода? Ты получил это! Я не уверен, насколько это будет полезно; это настолько ванильно, насколько это возможно (около 75% шаблонного), но я полагаю, что это обеспечит некоторый контекст. Я понимаю, что использую QStringList и что для этого есть QStringListModel, но QStringList, который я использую для хранения данных, является заполнителем - модель в конечном итоге будет несколько сложнее, поэтому, в конце концов, я необходимо использовать пользовательскую модель, полученную из QAbstractItemModel.

//
// wordlistmodel.h ///////////////////////////////////////
//
class WordListModel : public QAbstractItemModel
{
    Q_OBJECT
public:
    WordListModel(QObject* parent = 0);

    virtual QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const;
    virtual QModelIndex parent(const QModelIndex& index) const;
    virtual int rowCount(const QModelIndex& parent = QModelIndex()) const;
    virtual int columnCount(const QModelIndex & parent = QModelIndex()) const;
    virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;

public slots:
    void loadWords();

signals:
    void wordAdded();

private:
    // TODO: this is a temp backing store for the data
    QStringList wordList;
};


//
// wordlistmodel.cpp ///////////////////////////////////////
//
WordListModel::WordListModel(QObject* parent) :
    QAbstractItemModel(parent)
{
    wordList.reserve(1605572 + 50); // testing purposes only!
}

void WordListModel::loadWords()
{
    // load items from file or database

    // Due to taking Kuba Ober's advice to call setUniformItemSizes(true),
    // loading is fast. I'm not using a background thread to do
    // loading because I was trying to visually benchmark loading speed.
    // Besides, I am going to use a completely different method using
    // an in-memory file or a database, so optimizing this loading by
    // putting it in a background thread would obfuscate things.
    // Loading isn't a problem or the point of my question; it takes
    // less than a second to load all 1.6 million items.

    QFile file("german.dic");
    if (!file.exists() || !file.open(QIODevice::ReadOnly))
    {
        QMessageBox::critical(
            0,
            QString("File error"),
            "Unable to open " + file.fileName() + ". Make sure it can be located in " +
                QDir::currentPath()
        );
    }
    else
    {
        QTextStream stream(&file);
        int numRowsBefore = wordList.size();
        int row = 0;
        while (!stream.atEnd())
        {
            // This works for testing, but it's not optimal.
            // My real solution will use a completely different
            // backing store (memory mapped file or database),
            // so I'm not going to put the gory details here.
            wordList.append(stream.readLine());    

            ++row;

            if (row % 10000 == 0)
            {
                // visual benchmark to see how fast items
                // can be loaded. Don't do this in real code;
                // this is a hack. I know.
                emit wordAdded();
                QApplication::processEvents();
            }
        }

        if (row > 0)
        {
            // update final word count
            emit wordAdded();
            QApplication::processEvents();

            // It's dumb that I need to know how many items I
            // am adding *before* calling beginInsertRows().
            // So my begin/end block is empty because I don't know
            // in advance how many items I have, and I don't want
            // to pre-process the list just to count the number
            // of items. But, this gets the job done.
            beginInsertRows(QModelIndex(), numRowsBefore, numRowsBefore + row - 1);
            endInsertRows();
        }
    }
}

QModelIndex WordListModel::index(int row, int column, const QModelIndex& parent) const
{
    if (row < 0 || column < 0)
        return QModelIndex();
    else
        return createIndex(row, column);
}

QModelIndex WordListModel::parent(const QModelIndex& index) const
{
    return QModelIndex(); // this is used as the parent index
}

int WordListModel::rowCount(const QModelIndex& parent) const
{
    return wordList.size();
}

int WordListModel::columnCount(const QModelIndex& parent) const
{
    return 1; // it's a list
}

QVariant WordListModel::data(const QModelIndex& index, int role) const
{
    if (!index.isValid())
    {
        return QVariant();
    }    
    else if (role == Qt::DisplayRole)
    {
        return wordList.at(index.row());
    }
    else
    {    
        return QVariant();
    }
}


//
// mainwindow.h ///////////////////////////////////////
//    
class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

public slots:
    void updateWordCount();

private:
    Ui::MainWindow *ui;
    WordListModel* wordListModel;
};

//
// mainwindow.cpp ///////////////////////////////////////
//
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    ui->listView->setModel(wordListModel = new WordListModel(this));

    // this saves TONS of time during loading,
    // but selecting/scrolling performance wasn't improved
    ui->listView->setUniformItemSizes(true);

    // these didn't help selecting/scrolling performance...
    //ui->listView->setLayoutMode(QListView::Batched);
    //ui->listView->setBatchSize(100);

    connect(
        ui->pushButtonLoadWords,
        SIGNAL(clicked(bool)),
        wordListModel,
        SLOT(loadWords())
    );

    connect(
        wordListModel,
        SIGNAL(wordAdded()),
        this,
        SLOT(updateWordCount())
    );
}

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

void MainWindow::updateWordCount()
{
    QString wordCount;
    wordCount.setNum(wordListModel->rowCount());
    ui->labelNumWordsLoaded->setText(wordCount);
}

Как уже отмечалось, я уже рассмотрел и принял совет Кубы Обера:

QListView слишком долго обновляется при наличии 100 тыс. элементов

Мой вопрос не дублирует этот вопрос! В другом вопросе ОП спрашивал о скорости загрузки, которая, как я отметил в своем коде выше, не проблема из-за вызова setUniformItemSizes(true).

Сводные вопросы

  1. Почему навигация по QListView (с миллионами элементов в модели) с помощью клавиатуры происходит так медленно, когда список прокручивается?
  2. Почему сочетание выбора и прокрутки элементов вызывает замедление?
  3. Есть ли какие-либо детали реализации, которые я упустил, или я достиг порога производительности для QListView?

person Matthew Kraus    schedule 08.09.2015    source источник
comment
Есть ли смысл показывать миллион?   -  person user2672165    schedule 08.09.2015
comment
эти миллионы элементов помещаются в представление одновременно? Если нет, у вас может быть место для оптимизации. Показать свою работу.   -  person UmNyobe    schedule 08.09.2015
comment
возможно, вам нужно улучшить производительность вашей модели. Помните, что QListView все время обращается к модели. Вы также можете вставить фильтр, чтобы уменьшить количество добавляемых элементов. Список с миллионами пунктов не очень полезен.   -  person eferion    schedule 08.09.2015
comment
Подумайте о том, чтобы отправить отчет об ошибке.   -  person n. 1.8e9-where's-my-share m.    schedule 08.09.2015
comment
@UmNyobe Не все элементы помещаются в поле зрения. Любой список, содержащий, скажем, более 100 элементов, вряд ли уместится на любом экране одновременно. Это то, что должно обеспечить разделение архитектуры модель-представление — вы можете иметь множество элементов, а затем выбирать, как они отображаются. Я выбираю показать их в виде списка. В любом случае, я обновил свой ответ, чтобы показать свою работу, как вы просили.   -  person Matthew Kraus    schedule 09.09.2015
comment
@eferion, производительность модели очень быстрая. Пожалуйста, смотрите мой обновленный ответ.   -  person Matthew Kraus    schedule 09.09.2015
comment
@н.м. Я пока не уверен, что это ошибка. Однако, возможно, я раздвигаю границы того, с чем могут справиться QListView и QAbstractItemModel, что я и пытаюсь выяснить. Я хочу докопаться до первопричины.   -  person Matthew Kraus    schedule 09.09.2015
comment
@KubaOber Я не уверен, что такое нормальная навигация, но фильтры можно использовать для того, о чем, я думаю, вы говорите. Фильтрация списка - это другая проблема, помимо того, что я пытаюсь здесь сделать. В моем обновленном примере я показываю список немецких слов. В конечном итоге пользователи смогут отфильтровать список до интересующих их элементов, но затем, когда фильтр будет очищен, текущий выбранный элемент останется выбранным. Идея состоит в том, чтобы позволить пользователям просматривать похожие слова в алфавитном порядке вокруг их выбора. Пользовательский опыт выходит за рамки моего вопроса.   -  person Matthew Kraus    schedule 09.09.2015
comment
@KubaOber Первоначально я должен был упомянуть, что просмотрел ваш пост на stackoverflow.com/a/18579171/2452084. Это был мой недостаток, я должен был сослаться на это. Не могли бы вы просмотреть мой обновленный ответ? Я попытался учесть здесь ваш мудрый совет, и мой вопрос основывается на извлеченных вами уроках. Тем не менее, мой вопрос требует глубокого погружения в другую проблему производительности, и в духе того, как идет бизнес на SO, я не хотел захватывать другой поток OP. Итак, в свете моего обновленного ответа, не могли бы вы снять пометку с этого вопроса как с дубликатом, пожалуйста?   -  person Matthew Kraus    schedule 09.09.2015
comment
Очевидно, что если прокрутка мыши работает, а прокрутка клавиатуры — нет, это не проблема с ограничениями.   -  person n. 1.8e9-where's-my-share m.    schedule 09.09.2015
comment
@н.м. Кажется, что Qt должен справиться с этим, верно? Я просто не понимаю, почему выбор + прокрутка приводит к такому снижению производительности. Ясно, что извлечение данных из модели, а также визуализация изображения — это быстрые операции.   -  person Matthew Kraus    schedule 09.09.2015
comment
Например, внешние интерфейсы базы данных часто ограничивают количество отображаемых строк.   -  person user2672165    schedule 09.09.2015
comment
Я хочу убедиться, что правильно вас понял: у вас все еще есть проблема?   -  person Kuba hasn't forgotten Monica    schedule 09.09.2015
comment
@KubaOber Да, проблема все еще сохраняется. Ваше предложение улучшило время загрузки, но производительность выбора и прокрутки по-прежнему остается проблемой.   -  person Matthew Kraus    schedule 09.09.2015
comment
По сути, вся эта производительность — давняя ошибка. Нет ничего плохого в архитектуре модель-представление, которая вызывает это. QListView в этом отношении просто недоработан - в нем нет ничего, что ограничивало бы его производительность, просто текущая реализация несовершенна. Я бы посоветовал вам получить учетную запись gerrit, настроить git и исправить ее. В любом случае вам нужно будет собрать Qt из исходного кода, чтобы отследить Qt и диагностировать, почему он работает медленно, поэтому его исправление находится недалеко от этого :)   -  person Kuba hasn't forgotten Monica    schedule 09.09.2015
comment
@KubaOber Спасибо за фон. У меня еще нет учетной записи gerrit, но я собрал Qt из git. Недавно я скомпилировал Qt 5.6 для Visual Studio 2015, поэтому я уверен, что можно использовать профилировщик VS для обнаружения горячих путей. Исправление реализации может быть за пределами моей досягаемости на данный момент, но, по крайней мере, немного утешительно понимать, что это давняя ошибка с QListView, а не то, что можно легко исправить. Если я получу пропускную способность, это будет интересная попытка...   -  person Matthew Kraus    schedule 09.09.2015
comment
Боже мой, до какого полнейшего абсурда я дожил, приведя визуальную студию в качестве примера эффективности реализации. Тем более, что QtWidgets настолько зрелы и считаются готовыми, что в них уже нечего улучшать. Та же старая история — разработчики слишком заняты добавлением новых ошибок и наворотов…   -  person dtech    schedule 16.06.2018
comment
Обратите внимание, что эта проблема не воспроизводится в Linux (по крайней мере, при использовании LInux Mint). Здесь с помощью стрелок для навигации по полосе прокрутки отображается время с постоянной скоростью, даже когда представление начинает прокручиваться для отображения новых элементов.   -  person jpo38    schedule 17.06.2018


Ответы (3)


<сильный>1. Почему навигация по QListView (с миллионами элементов в модели) с помощью клавиатуры происходит так медленно при прокрутке списка?

Потому что, когда вы перемещаетесь по списку с помощью клавиатуры, вы вводите внутреннюю функцию Qt QListModeViewBase::perItemScrollToValue, см. стек:

Qt5Widgetsd.dll!QListModeViewBase::perItemScrollToValue(int index, int scrollValue, int viewportSize, QAbstractItemView::ScrollHint hint, Qt::Orientation orientation, bool wrap, int itemExtent) Ligne 2623    C++
Qt5Widgetsd.dll!QListModeViewBase::verticalScrollToValue(int index, QAbstractItemView::ScrollHint hint, bool above, bool below, const QRect & area, const QRect & rect) Ligne 2205  C++
Qt5Widgetsd.dll!QListViewPrivate::verticalScrollToValue(const QModelIndex & index, const QRect & rect, QAbstractItemView::ScrollHint hint) Ligne 603    C++
Qt5Widgetsd.dll!QListView::scrollTo(const QModelIndex & index, QAbstractItemView::ScrollHint hint) Ligne 575    C++
Qt5Widgetsd.dll!QAbstractItemView::currentChanged(const QModelIndex & current, const QModelIndex & previous) Ligne 3574 C++
Qt5Widgetsd.dll!QListView::currentChanged(const QModelIndex & current, const QModelIndex & previous) Ligne 3234 C++
Qt5Widgetsd.dll!QAbstractItemView::qt_static_metacall(QObject * _o, QMetaObject::Call _c, int _id, void * * _a) Ligne 414   C++
Qt5Cored.dll!QMetaObject::activate(QObject * sender, int signalOffset, int local_signal_index, void * * argv) Ligne 3732    C++
Qt5Cored.dll!QMetaObject::activate(QObject * sender, const QMetaObject * m, int local_signal_index, void * * argv) Ligne 3596   C++
Qt5Cored.dll!QItemSelectionModel::currentChanged(const QModelIndex & _t1, const QModelIndex & _t2) Ligne 489    C++
Qt5Cored.dll!QItemSelectionModel::setCurrentIndex(const QModelIndex & index, QFlags<enum QItemSelectionModel::SelectionFlag> command) Ligne 1373    C++

И эта функция делает:

itemExtent += spacing();
QVector<int> visibleFlowPositions;
visibleFlowPositions.reserve(flowPositions.count() - 1);
for (int i = 0; i < flowPositions.count() - 1; i++) { // flowPositions count is +1 larger than actual row count
    if (!isHidden(i))
        visibleFlowPositions.append(flowPositions.at(i));
}

Где flowPositions содержит столько же элементов, сколько и ваш QListView, так что это в основном перебирает все ваши элементы, и это определенно займет некоторое время для обработки.

<сильный>2. Почему сочетание выбора и прокрутки элементов приводит к замедлению работы?

Потому что «выбор и прокрутка» заставляет Qt вызывать QListView::scrollTo (для прокрутки представления к определенному элементу), и это то, что заканчивается вызовом QListModeViewBase::perItemScrollToValue. Когда вы прокручиваете с помощью полосы прокрутки, системе не нужно просить представление прокрутить до определенного элемента.

<сильный>3. Есть ли какие-либо детали реализации, которые я упустил, или я достиг порога производительности для QListView?

Боюсь, вы все делаете правильно. Это определенно ошибка Qt. Отчет об ошибке должен быть сделан, чтобы надеяться, что это будет исправлено в более поздних выпусках. Я отправил сюда ошибку Qt.

Поскольку этот код является внутренним (закрытые классы данных) и не зависит от каких-либо настроек QListView, я не вижу способа исправить его, кроме как путем изменения и перекомпиляции исходного кода Qt (но я точно не знаю, как это сделать, это потребует дополнительных исследований) . Первая функция, которую можно переопределить в стеке, это QListView::scrollTo, но я сомневаюсь, что было бы легко переопределить ее, не вызывая QListViewPrivate::verticalScrollToValue...

Примечание. Тот факт, что эта функция проходит через все элементы представления, по-видимому, появился в Qt 4.8.3, когда эта ошибка исправлена ​​(см. изменения). По сути, если вы не скрываете какие-либо элементы в своем представлении, вы можете изменить код Qt, как показано ниже:

/*QVector<int> visibleFlowPositions;
visibleFlowPositions.reserve(flowPositions.count() - 1);
for (int i = 0; i < flowPositions.count() - 1; i++) { // flowPositions count is +1 larger than actual row count
    if (!isHidden(i))
        visibleFlowPositions.append(flowPositions.at(i));
}*/
QVector<int>& visibleFlowPositions = flowPositions;

Затем вам придется перекомпилировать Qt, и я уверен, что это решит проблему (однако не тестировалось). Но затем вы увидите новые проблемы, если однажды скроете некоторые элементы... например, для поддержки фильтрации!

Скорее всего, правильным решением было бы поддерживать в представлении как flowPositions, так и visibleFlowPositions, чтобы не создавать его на лету...

person jpo38    schedule 18.06.2018
comment
Это настолько хороший ответ, насколько я могу надеяться на данный момент, и вы даже отправили отчет об ошибке (спасибо!), поэтому я отмечу это как принятый ответ. Будет интересно посмотреть, будет ли это учтено в будущих версиях Qt. Я не могу сказать, что понимаю назначение позиций потока в приведенном выше коде, но я могу оценить необходимость поддержки скрытых элементов. - person Matthew Kraus; 19.06.2018
comment
@MatthewKraus: Я тоже этого не понимаю. Странно, когда представление сохраняет контейнер, заканчивающийся огромным размером, в то время как использование модели предназначено для того, чтобы избежать этого.... - person jpo38; 19.06.2018
comment
@MatthewKraus: обратите внимание, что эту ошибку планируется исправить в Qt 5.11.1! - person jpo38; 28.06.2018

Я сделал следующий тест:

Прежде всего я создаю класс для проверки вызовов:

struct Test
{
  static void NewCall( QString function, int row )
  {
    function += QString::number( row );

    map[ function ]++;
  }

  static void Summary( )
  {
    qDebug() << "-----";
    int total = 0;
    QString data;
    for( auto pair : map )
    {
      data = pair.first + ": " + QString::number( pair.second );
      total += pair.second;
      qDebug( ) << data;
    }

    data = "total: " + QString::number( total ) + " calls";
    qDebug() << data;
    map.clear();
  }

  static std::map< QString, int > map;
};

std::map<QString,int> Test::map;

Затем я вставляю вызов NewCall в методы index, parent и data WordListModel. Наконец, я добавляю QPushButton в диалоговое окно, сигнал clicked связан с методом, который вызывает Test::Summary.

Этапы теста следующие:

  1. Выберите последний показанный элемент списка
  2. Нажмите кнопку Сводка, чтобы очистить список вызовов.
  3. С помощью клавиши tab снова выберите представление списка.
  4. Выполните прокрутку с помощью клавиш направления
  5. Нажмите кнопку Сводка еще раз.

Распечатанный список показывает проблему. Виджет QListView совершает большое количество звонков. Кажется, виджет перезагружает все данные из модели.

Я не знаю, можно ли его улучшить, но вы ничего не можете сделать, кроме как отфильтровать список, чтобы ограничить количество отображаемых элементов.

person eferion    schedule 08.09.2015
comment
Ух ты. QListView действительно чрезмерно запрашивает модель. Кажется, что вы даже не можете навести указатель мыши на виджет QListView без сотен звонков. Я попробовал этот тест: выберите первый элемент, нажмите клавишу END, чтобы перейти к последнему элементу в списке, затем нажмите кнопку Summary. Было сделано 3 211 774 звонка с ~ 1,6 миллиона товаров! (Загрузка делает 3 211 323 вызовов.) Он знает, сколько существует элементов, он должен иметь возможность быстро переходить к первому/последнему элементу и запрашивать только те элементы, которые находятся в поле зрения, но похоже, что запрашивается каждый элемент, как и вы. сказать. - person Matthew Kraus; 10.09.2015
comment
Используя вашу тестовую программу, я измерил количество вызовов, сделанных при использовании мыши для перемещения полосы прокрутки (см. раздел «Быстрая прокрутка с помощью МЫШИ» в моем вопросе). Кажется, что прокрутка мышью выполняется довольно быстро, для средней прокрутки списка (сверху вниз) требуется всего около 30 000 вызовов. По сравнению с тестом сверху вниз (нажатие END), о котором я упоминал в предыдущем комментарии, прокрутка мышью в 100 раз быстрее. - person Matthew Kraus; 10.09.2015
comment
Просто для справки, я выполнил тест, аналогичный вашему: перейдите в конец списка, нажмите Сводка, чтобы очистить статистику, выберите первое слово, нажмите ВВЕРХ, затем нажмите Сводка -- было 4 817 168 звонков. Это примерно на 1,6 миллиона вызовов БОЛЬШЕ, чем при первоначальной загрузке всего списка. - person Matthew Kraus; 10.09.2015
comment
Я не уверен, что это ошибка Qt. Я не знаю, можно ли решить эту проблему оптимизацией кода Qt, но это серьезная проблема, если вам нужно показать список с очень большим количеством элементов. - person eferion; 10.09.2015
comment
Возможно, QListView имеет больше возможностей, чем мне нужно. Кажется, что создание пользовательского виджета для отображения большого количества элементов может стоить усилий. Я решил, что попытаюсь использовать готовое решение, прежде чем запускать свое собственное, но в этом случае может иметь смысл собственный виджет. - person Matthew Kraus; 10.09.2015

К сожалению, я считаю, что вы мало что можете с этим поделать. У нас нет особого контроля над виджетами.

Хотя вы можете избежать этой проблемы, используя вместо этого ListView. Если вы попробуете мой краткий пример ниже, вы заметите, насколько быстрым он может быть даже при использовании дорогостоящих делегатов.

Вот пример:

Window{
    visible: true
    width: 200
    height: 300

    property int i: 0;

    Timer {
        interval: 5
        repeat: true
        running: true
        onTriggered: {
            i += 1
            lv.positionViewAtIndex(i, ListView.Beginning)
        }
    }

    ListView {
        id:lv
        anchors.fill: parent
        model: 1605572
        delegate: Row {
            Text { text: index; width: 300; }
        }
    }
}

Я поставил Timer для имитации прокрутки, но, конечно, вы можете включить или выключить этот таймер в зависимости от того, нажаты ли клавиши, а также изменить i += 1 на i += -1, если вместо . Вам также придется добавить проверки переполнения и потери значимости.

Вы также можете выбрать скорость прокрутки, изменив interval на Timer. Тогда это просто вопрос изменения цвета выбранного элемента и т. д., чтобы показать, что он выбран.

Кроме того, вы можете использовать cacheBuffer с ListView для кэширования большего количества элементов, но я не думаю, что это необходимо.

Если вы все равно хотите использовать QListView, взгляните на этот пример: http://doc.qt.io/qt-5/qtwidgets-itemviews-fetchmore-example.html Использование метода выборки позволяет сохранить производительность даже при работе с большими наборами данных. Это позволяет вам заполнять список по мере прокрутки.

person ALDSMQJF    schedule 15.06.2018