Russian Qt Forum

Qt => Qt Quick => Тема начата: nono от Март 31, 2015, 21:34



Название: медленная сортировка и фильтрация данных в TableView
Отправлено: nono от Март 31, 2015, 21:34
Для реализации таблицы использую элемент TableView. Сама модель реализована на базе QSqlQueryModel
Вот часть кода модели
Код:
ModelDevices::ModelDevices(QSqlDatabase &db, QObject *parent): query(db), QSqlQueryModel(parent)
{
    int index(1);
    roles[Qt::UserRole + index++] += "device_id";
    roles[Qt::UserRole + index++] += "section";
    roles[Qt::UserRole + index++] += "division";
    roles[Qt::UserRole + index++] += "power_center";
    roles[Qt::UserRole + index++] += "group_name";
    roles[Qt::UserRole + index++] += "net_address";
    roles[Qt::UserRole + index++] += "personal_account";
    roles[Qt::UserRole + index++] += "payment_code";
    roles[Qt::UserRole + index++] += "accounting_type";
    roles[Qt::UserRole + index++] += "settlement";
    roles[Qt::UserRole + index++] += "street";
    roles[Qt::UserRole + index++] += "house";
    roles[Qt::UserRole + index++] += "consumer_name";
    roles[Qt::UserRole + index++] += "serial_number";
    roles[Qt::UserRole + index++] += "repeater";
    roles[Qt::UserRole + index++] += "coefficient";
    roles[Qt::UserRole + index++] += "device_type";
    roles[Qt::UserRole + index++] += "ra_id";
    roles[Qt::UserRole + index++] += "gateway_number";
    roles[Qt::UserRole + index++] += "device_status";
    roles[Qt::UserRole + index++] += "askue";
    roles[Qt::UserRole + index++] += "note";
    roles[Qt::UserRole + index++] += "device_removed";
    roles[Qt::UserRole + index++] += "object_id";
}
//==============================================
QVariant ModelDevices::data(const QModelIndex &item, int role) const
{
    QVariant value;
    if (role < Qt::UserRole) {
        value = QSqlQueryModel::data(item, role);
    } else {
        int columnIndex = role - Qt::UserRole - 1;
        QModelIndex modelIndex = this->index(item.row(), columnIndex);
        value = QSqlQueryModel::data(modelIndex, Qt::DisplayRole);
    }
    return value;
}
//==============================================
QHash<int, QByteArray> ModelDevices::roleNames() const
{
    return roles;
}
//==============================================
Для осуществления сортировки использую модель на базе QSortFilterProxyModel
Вот код
Код:
SortFilterProxyModel::SortFilterProxyModel(QObject *parent) : QSortFilterProxyModel(parent), m_complete(false)
{
    connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SIGNAL(countChanged()));
    connect(this, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SIGNAL(countChanged()));
}

int SortFilterProxyModel::count() const
{
    return rowCount();
}

QObject *SortFilterProxyModel::source() const
{
    return sourceModel();
}

void SortFilterProxyModel::setSource(QObject *source)
{
    setSourceModel(qobject_cast<QAbstractItemModel *>(source));
}

QByteArray SortFilterProxyModel::sortRole() const
{
    return m_sortRole;
}

void SortFilterProxyModel::setSortRole(const QByteArray &role)
{
    if (m_sortRole != role) {
        m_sortRole = role;
        if (m_complete)
            QSortFilterProxyModel::setSortRole(roleKey(role));
    }
}

void SortFilterProxyModel::setSortOrder(Qt::SortOrder order)
{
    QSortFilterProxyModel::sort(0, order);
}

QByteArray SortFilterProxyModel::filterRole() const
{
    return m_filterRole;
}

void SortFilterProxyModel::setFilterRole(const QByteArray &role)
{
    if (m_filterRole != role) {
        m_filterRole = role;
        if (m_complete)
            QSortFilterProxyModel::setFilterRole(roleKey(role));
    }
}

QString SortFilterProxyModel::filterString() const
{
    return filterRegExp().pattern();
}

void SortFilterProxyModel::setFilterString(const QString &filter)
{
    setFilterRegExp(QRegExp(filter, filterCaseSensitivity(), static_cast<QRegExp::PatternSyntax>(filterSyntax())));
}

SortFilterProxyModel::FilterSyntax SortFilterProxyModel::filterSyntax() const
{
    return static_cast<FilterSyntax>(filterRegExp().patternSyntax());
}

void SortFilterProxyModel::setFilterSyntax(SortFilterProxyModel::FilterSyntax syntax)
{
    setFilterRegExp(QRegExp(filterString(), filterCaseSensitivity(), static_cast<QRegExp::PatternSyntax>(syntax)));
}

QJSValue SortFilterProxyModel::get(int idx) const
{
    QJSEngine *engine = qmlEngine(this);
    QJSValue value = engine->newObject();
    if (idx >= 0 && idx < count()) {
        QHash<int, QByteArray> roles = roleNames();
        QHashIterator<int, QByteArray> it(roles);
        while (it.hasNext()) {
            it.next();
            value.setProperty(QString::fromUtf8(it.value()), data(index(idx, 0), it.key()).toString());
        }
    }
    return value;
}

void SortFilterProxyModel::classBegin()
{
}

void SortFilterProxyModel::componentComplete()
{
    m_complete = true;
    if (!m_sortRole.isEmpty())
        QSortFilterProxyModel::setSortRole(roleKey(m_sortRole));
    if (!m_filterRole.isEmpty())
        QSortFilterProxyModel::setFilterRole(roleKey(m_filterRole));
}

int SortFilterProxyModel::roleKey(const QByteArray &role) const
{
    QHash<int, QByteArray> roles = roleNames();
    QHashIterator<int, QByteArray> it(roles);
    while (it.hasNext()) {
        it.next();
        if (it.value() == role)
            return it.key();
    }
    return -1;
}

QHash<int, QByteArray> SortFilterProxyModel::roleNames() const
{
    if (QAbstractItemModel *source = sourceModel())
        return source->roleNames();
    return QHash<int, QByteArray>();
}

bool SortFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
{
    QRegExp rx = filterRegExp();
    if (rx.isEmpty())
        return true;
    QAbstractItemModel *model = sourceModel();
    if (filterRole().isEmpty()) {
        QHash<int, QByteArray> roles = roleNames();
        QHashIterator<int, QByteArray> it(roles);
        while (it.hasNext()) {
            it.next();
            QModelIndex sourceIndex = model->index(sourceRow, 0, sourceParent);
            QString key = model->data(sourceIndex, it.key()).toString();
            if (key.contains(rx))
                return true;
        }
        return false;
    }
    QModelIndex sourceIndex = model->index(sourceRow, 0, sourceParent);
    if (!sourceIndex.isValid())
        return true;
    QString key = model->data(sourceIndex, roleKey(filterRole())).toString();
    return key.contains(rx);
}
Вот код передачи объектов в форму qml
Код:
engine = new QQmlApplicationEngine(this);
    qmlRegisterType<SortFilterProxyModel>("org.qtproject.example", 1, 0, "SortFilterProxyModel");
    engine->load(QUrl("qrc:///Ui/MainWindow"));
    engine->rootContext()->setContextProperty("ram", this);
    engine->rootContext()->setContextProperty("modelGroups", modelGroups);
    engine->rootContext()->setContextProperty("modelObjects", modelObjects);
    engine->rootContext()->setContextProperty("modelDevices", modelDevices);
А вот код настройки прокси модели
Код:
  // Настройка прокси модели
    SortFilterProxyModel {
        id: proxyModelDevices
        source: modelDevices

        sortOrder: viewListDevices.sortIndicatorOrder
        //sortCaseSensitivity: Qt.CaseInsensitive
        sortRole: viewListDevices.getColumn(viewListDevices.sortIndicatorColumn).role

        filterString: textSearch.text
        filterSyntax: SortFilterProxyModel.Wildcard
        filterCaseSensitivity: Qt.CaseInsensitive
    }
Проблема заключается в сортировке и фильтре данных. Данные процессы происходят очень медленно, учитывая что в базе хранится несколько десятков тысяч записей. При этом С-шная реализация на базе QTableView с теми же самыми моделями производит фильтрацию и сортировку достаточно быстро. Как можно ускорить все это дело в qml?


Название: Re: медленная сортировка и фильтрация данных в TableView
Отправлено: Отражение луны от Март 31, 2015, 22:26
Я сразу скажу, что не знаю с++ и, следовательно, не знаю как работает проксимодель. Поэтому моё предположение будет основано на предположениях, как ни парадоксально. ListView, на котором основан TableView, отрисовывает только видимые элементы. Скорее всего при загрузке всего списка тормозов Вы не наблюдаете. А выглядит cbnefwbz так, будто при поиске или фильтрации удаление несоответствующих критериям поиска  элементов приводит к тому, что видимыми становятся ранее скрытые элементы, а именно каждый(в т.ч. удаляемые) из них, и происходит это до тех пор, пока прошедшие сортировку элементы полностью не заполнят собой табличное пространство. И QML старательно создает, разрушает и пытается отрисовать все то, что на самом деле в итоге будет отфильтровано. В таком случае Вы, возможно, оповещаете таблицу об изменении при каждом удалении отфильтрованного элемента, а не когда фильтрация отработала полностью.


Название: Re: медленная сортировка и фильтрация данных в TableView
Отправлено: nono от Март 31, 2015, 23:00
Я сразу скажу, что не знаю с++ и, следовательно, не знаю как работает проксимодель. Поэтому моё предположение будет основано на предположениях, как ни парадоксально. ListView, на котором основан TableView, отрисовывает только видимые элементы. Скорее всего при загрузке всего списка тормозов Вы не наблюдаете. А выглядит cbnefwbz так, будто при поиске или фильтрации удаление несоответствующих критериям поиска  элементов приводит к тому, что видимыми становятся ранее скрытые элементы, а именно каждый(в т.ч. удаляемые) из них, и происходит это до тех пор, пока прошедшие сортировку элементы полностью не заполнят собой табличное пространство. И QML старательно создает, разрушает и пытается отрисовать все то, что на самом деле в итоге будет отфильтровано. В таком случае Вы, возможно, оповещаете таблицу об изменении при каждом удалении отфильтрованного элемента, а не когда фильтрация отработала полностью.
У меня были уже такие предположения, потому что я уже все перепробовал.
Тогда возникает другой вопрос...как TableView заставить производить отрисовку, только после окончания фильтрации или сортировки модели?


Название: Re: медленная сортировка и фильтрация данных в TableView
Отправлено: Отражение луны от Апрель 01, 2015, 03:14
Полагаю, что адекватного способа нет (из неадекватных я бы предолжил попробовать изменить размеры на нулевые). У меня была похожая ситуация, и даже если компонент невиден почему-то делегаты все равно создавались. Если прокрутить невидимый список (listview) яваскриптом - аналогично. Поэтому можно сделать вывод, что дело даже не в отрисовке самой , большую часть времени занимает именно создание и разрушение делегатов, при этом qml даже может давать течь (наблюдал на 5.2.х). Имхо стоит попробовать:
1) полную очистку модели и заполнение её заново
2) на время выполнения фильтрации выставлять model = null
И то и другое, естественно, вернет список в начало и полностью пересоздаст строки таблицы.


Название: Re: медленная сортировка и фильтрация данных в TableView
Отправлено: nono от Апрель 01, 2015, 21:34
1) полную очистку модели и заполнение её заново
2) на время выполнения фильтрации выставлять model = null
И то и другое, естественно, вернет список в начало и полностью пересоздаст строки таблицы.
Да чет не очень получается....Короче я пришел к выводу что для реализации серьезных проектов qml еще сыроват...лучше использовать старые проверенные виджиты


Название: Re: медленная сортировка и фильтрация данных в TableView
Отправлено: Отражение луны от Апрель 02, 2015, 02:22
Могу лишь сказать, что у нас в проекте тоже QSqlQueryModel (но без qsortfilterproxymodel), фильтрация и сортировка осуществляется запросами, и отклик на поиск моментальный (~200 милисекунд максимум на самом слабом железе) при отображении ответа на ~5 к строк. Потому склонен думать, что проблема в чем-то еще.


Название: Re: медленная сортировка и фильтрация данных в TableView
Отправлено: nono от Апрель 02, 2015, 21:21
Могу лишь сказать, что у нас в проекте тоже QSqlQueryModel (но без qsortfilterproxymodel), фильтрация и сортировка осуществляется запросами, и отклик на поиск моментальный (~200 милисекунд максимум на самом слабом железе) при отображении ответа на ~5 к строк. Потому склонен думать, что проблема в чем-то еще.
Такой вариант меня бы устроил если бы не больше кол-во подключений к базе данных....суть приложения следующая есть куча серверов независимых друг от друга порядка 60.....на них ведутся базы....доступ на прямую к базе у меня нет, через командную строку я могу выгрузить необходимы данные в отчет....я сделал модуль, который устанавливается на сервере и по команде выгружает нужный отчет, команды этому модулю посылает клиент, он же и получает отчет (все происходит по сети)....дальше этот клиент должен предоставлять интерфейс работы с этими данными и выгрузки определенных отчетов....на нем то у меня и не получается ускорить сортировку.....а запросы при сортировке я не использую потому, что обращения к серверам у меня происходит в многопоточном режиме, соответственно полученные данные и запись их в базу тоже в многопоточном режиме, для этого я открываю сразу нескольких подключений с одного клиента (с учетом основного до 30 одновременных подключений и транзакций).....как бы все хорошо, но клиент не один, количество соединений соответственно возрастает в разы...и по этому я стараюсь лишний раз к базе не тыкаться...пришел к решению прокси модели