QListView with millions of items slower with a keyboard

I am using QListViewwith a custom model derived from QAbstractItemModel. I have about millions of items. I called listView->setUniformItemSizes(true)to prevent the layout logic heap from being called when I add elements to the model. So far, everything is working as expected.

The problem is that using the keyboard to move through the list is slower . If I select an item in the list and then press up / down, the selection moves quickly until the selection is needed to scroll through the list. Then he becomes extremely backward. Pressing the up or down button is also very laggy. It seems that the problem is when the item is selected (as the "current item") with the keyboard, and the list also scrolls up / down.

If I use a mouse, moving through the list is fast. I can use the mouse wheel that quickly. I can drag the scroll bar up / down as fast as I want - from the top of the list to the bottom - and the list view updates maliciously fast.

Any ideas on why the combination of changing choices and scrolling through the list is so slow? Is there a viable job?

Update 9/9/15

To better illustrate this issue, I am providing extended information in this update.

Performance issues with KEYBOARD + SCROLLING

This is mainly a performance issue, although it is slightly related to the user interface (UX). Check what happens when I use the keyboard to scroll through the page QListView:

Slow scrolling

Pay attention to the slowdown at the bottom? This is the focus of my question. Let me explain how I view the list.

Explanation:

  • Starting at the top, the first item in the list is selected.
  • When you press and hold the down arrow key, the current item (selection) changes to the next item.
  • , .
  • , .

, , - , , , .

MOUSE

, :

Quick mouse navigation

:

  • , .
  • , .
  • .
  • , .

:

  • . , . , .

  • . " " ( ) . , Qt - , .

-Qt FAST

, , , Qt.

. , , . , , juce:: ListBoxModel juce:: ListBox. (, , QModelIndex , ). , Qt QModelIndex , , , , . , , QModelIndex es .

JUCE "" "" , . Qt QListView, , .

JUCE . Qt QListView ?!

, ? , :

Visual Studio Index

Visual Studio. , , , ! , "", , , , . . , , .

? , . "" 150 . ( ), , - , - , .

1,7 ( ). , , ( ) text corpus, . .

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 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 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 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 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 , 100k

! OP , , , - setUniformItemSizes(true).

  • QListView ( ) , ?
  • ?
  • - , , QListView?
+4
1

:

, :

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.

:

  • "", .
  • ""

. QListView . , .

, , , , .

+1

Source: https://habr.com/ru/post/1606346/


All Articles