M
MarkGlenJune 26, 2020, 9:27 a.m.

Сортировка и фильтрация в QTableView, связь с моделью через Custom QSortFilterProxyModel

QTableView, QSortFilterProxyModel

Имеется QSqlQueryModel, делаю proxyModel->setSourceModel(model) и view->setModel(proxyModel).

view->setSortingEnabled(true) работает отлично, но пытаюсь по клику на строчку получить данные и понимаю что сортируется только view, а в модели сортировка не меняется.

То есть если была таблица с данными по возрастанию row1, row2, row3; я отсортировал row3, row2, row1 - по клику на row3 получаю доступ к row1.

Если у кого-то есть понимание поделитесь пожалуйста.

upd: помогли подсказки и поддержка пользователей и этот найденный пример.

custom_sort_filter_proxy_model.h

#include <QApplication>
#include <QtWidgets>

class NameYearFilterProxyModel : public QSortFilterProxyModel{
    Q_OBJECT
    QRegExp nameRegExp;
    QRegExp yearRegExp;
public:
    explicit NameYearFilterProxyModel(QObject* parent= nullptr):
        QSortFilterProxyModel(parent)
    {
        nameRegExp.setCaseSensitivity(Qt::CaseInsensitive);
        yearRegExp.setCaseSensitivity(Qt::CaseInsensitive);
        yearRegExp.setPatternSyntax(QRegExp::RegExp);
        nameRegExp.setPatternSyntax(QRegExp::RegExp);
    }

    bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const  override{
        QModelIndex nameIndex= sourceModel()->index(sourceRow, 0, sourceParent);
        QModelIndex yearIndex= sourceModel()->index(sourceRow, 1, sourceParent);

        QString name= sourceModel()->data(nameIndex).toString();
        QString year= sourceModel()->data(yearIndex).toString();

        return (

            name.contains(nameRegExp) && year.contains(yearRegExp)

            );
    }
public slots:
    void setNameFilter(const QString& regExp){
        nameRegExp.setPattern(regExp);
        invalidateFilter();
    }
    void setYearFilter(const QString& regExp){
        yearRegExp.setPattern(regExp);
        invalidateFilter();
    }
};

custom_sort_filter_proxy_model.cpp

#include "custom_sort_filter_proxy_model.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    //set up GUI
    QWidget w;
    w.setWindowTitle("Custom SortFilterProxyModel");

    QVBoxLayout layout(&w);
    QHBoxLayout hLayout;
    QLineEdit lineEditName;
    QLineEdit lineEditYear;

    lineEditName.setPlaceholderText("name filter");
    lineEditYear.setPlaceholderText("year filter");
    lineEditYear.setValidator(new QRegExpValidator(QRegExp("[0-9]*")));
    lineEditYear.setMaxLength(4);
    hLayout.addWidget(&lineEditName);
    hLayout.addWidget(&lineEditYear);

    QTableView tableView;
    layout.addLayout(&hLayout);
    layout.addWidget(&tableView);

    //set up models
    QStandardItemModel sourceModel;
    NameYearFilterProxyModel filterModel;;
    filterModel.setSourceModel(&sourceModel);
    tableView.setModel(&filterModel);

    QObject::connect(&lineEditName, &QLineEdit::textChanged,
                     &filterModel, &NameYearFilterProxyModel::setNameFilter);
    QObject::connect(&lineEditYear, &QLineEdit::textChanged,
                     &filterModel, &NameYearFilterProxyModel::setYearFilter);


    QVector<QString> names{"Danny", "Christine", "Lars",
                           "Roberto", "Maria"};
    for(int i=0; i<100; i++){
        QList<QStandardItem*> row;
        row.append(new QStandardItem(names[i%names.size()]));
        row.append(new QStandardItem(QString::number((i%9)+1980)));
        sourceModel.appendRow(row);
    }
    w.show();
    return a.exec();
}

Так же решение со сбитой нумерацией строк в комментариях.

We recommend hosting TIMEWEB
We recommend hosting TIMEWEB
Stable hosting, on which the social network EVILEG is located. For projects on Django we recommend VDS hosting.
21

А как получаете данные? Код приведите.

M

Возможно даже лишнего немного оставил, в целом картина такая:

// получаю данные
QSqlQuery query;
QString str;
        str=("SELECT bla-bla-bla FROM bla "
             "LEFT JOIN  bla "
             "ON bla-bla-bla.. ");
        query.exec(str);

model = new QSqlQueryModel;
model->setQuery(query);

// для фильтрации по колонкам
proxyOneModel->setSourceModel(model);
proxyTwoModel->setSourceModel(proxyOneModel);
proxyThreeModel->setSourceModel(proxyTwoModel);
proxyFourModel->setSourceModel(proxyThreeModel);
proxyFiveModel->setSourceModel(proxyFourModel);

view->setModel(proxyFiveModel);

// Включаю сортировку
view->setSortingEnabled(true);

По клику на одну из колонок

void dataBrowser::onTableClicked(const QModelIndex &index)
{
    if (index.isValid()) {
        int columnNumber = index.column();
        int rowNumber = index.row();

        if (columnNumber==2)
        {
            QModelIndex i = model->index(rowNumber, 0); // The first column data.
            emit setIdFromMain(i.data().toInt()); // по id записи вытаскиваю запросом строчку 
            //в другое место, то есть тут получаю не id строчки которую вижу в таблице 
            //а id который соответствует первоначальной сортировке по умолчанию
        }
    }
}
 connect(view, SIGNAL(clicked(const QModelIndex &)), this, SLOT(onTableClicked(const QModelIndex &)));

Я прошу прощения если от кода глаза слезятся, я учусь.

AC

Без кода сложно сказать.
Лучше всего создать к примеру свои классы

BaseSqlQueryModel *model        = new BaseSqlQueryModel(strQuery, ui->tableView); // класс для обработки вывода данных
BaseSortFilterProxyModel *proxy = new BaseSortFilterProxyModel(this);             // класс для сортировки
model->setQuery(getStrQuery());
proxy->setSourceModel(model);
ui->tableView->setModel(proxy);

В этих классах переопределить виртуальные методы ... взависимости от кода и проекта.

M

код я тут привёл https://evileg.com/en/forum/topic/1421/#post-8028

Страшно даже думать об этом, но если нет стандартных решений то придётся учиться.

Спасибо за ответ!

AC

Выложети весь код.
Не понятно proxyOneModel ...
С вашего кода я понимаю (годаю) что proxyOneModel унаследован от QSqlQueryModel
Вы должны использовать класс QSortFilterProxyModel

QSqlQueryModel *model        = new QSqlQueryModel;
QSortFilterProxyModel *proxy = new QSortFilterProxyModel(this);
proxy->setSourceModel(model);
ui->tableView->setModel(proxy);

Сортировка должна происходить в proxy , получать данные из model

M

Пытался адаптировать, чтобы не вникать в подробности, типа правила хорошего тона. Позже выложу по максимуму тогда. p.s., да, proxy это QSortFilterProxyModel

M

весь код как есть добавил в тело поста

M

Так. Убрал позорятину свою и без неё теперь видно. Заметил ошибку

тут model заменил на proxy:

QModelIndex i = model->index(rowNumber, 0); // The first column data.

всё заработало как надо!

спасибо всем кто отреагировал!!!

AC

Да, не успел указать.

M

Спасибо! :) Фух, достала эта ошибка.

А что по решаемой задаче должно получаться? У меня такое чувство, что вы лишних моделей QSortFilterProxy понасоздавали и всё можно было в одну модель засунуть.

M

Может быть и лишние, но не понял как по-другому. Каждая модель для отдельного фильтра

ведь можно установить только один FilterKeyColumn для модели:

proxyTimeModel->setFilterKeyColumn(4);

Посмотрите в сторону кастомной модели от QSortFilterProxyModel. В примерах Qt есть, Custom Sort/Filter Model Example. Так можно реализовать фильтрацию по нескольким полям, как вам и надо.

M

Попробую и тогда отпишусь по результату.

AC

По поводу вашего когда.
1 - Согласен с МаркГлен ​...

Посмотрите в сторону кастомной модели от QSortFilterProxyModel. В примерах Qt есть, Custom Sort/Filter Model Example. Так можно реализовать фильтрацию по нескольким полям, как вам и надо.

2 - Когда вы определяете/устанавливаете ширину колонок лучше использовать локальные константы (код лучше читается).
Пример:

#include "mainwindow.h"
#include "ui_mainwindow.h"

static const int COLUMN_ID = 0;   // ------- !!!!!!!!!!!!!!!!!
static const int COLUMN_NAME = 1; // ------- !!!!!!!!!!!!!!!!!

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{

}

3 - По поводу connect используете старый синтаксис.

connect(viewWindow, SIGNAL(triggered()), this, SLOT(show()));     // старый синтаксис
connect(viewWindow, &QAction::triggered, this, &NameClass::show); // новый синтаксис
AC

Забегая наперед - у вас возникла ещё одна проблема с сортировкой нумерации строк.
Как уже было сказано ...

Посмотрите в сторону кастомной модели от QSortFilterProxyModel

Необходимо переопределить виртуальный метод ...

QVariant BaseSortFilterProxyModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    // переопределение сортировки нумерации строк
    if (role == Qt::DisplayRole){
        if (orientation == Qt::Vertical || section == 0){
            return section + 1;
        }
    }
    return QSortFilterProxyModel::headerData(section, orientation, role);
}
M

Посмотрите в сторону кастомной модели от QSortFilterProxyModel

Сложновато для меня, не понимаю что надо делать и что происходит. Наверное мне ещё рано.

Собезъянничал вот так:

basesortfilterproxymodel.h

#include <QSortFilterProxyModel>
#include <QVariant>

class BaseSortFilterProxyModel : public QSortFilterProxyModel
{
public:
    BaseSortFilterProxyModel(QObject *parent = nullptr);
    QVariant headerData(int section, Qt::Orientation orientation, int role);
};

basesortfilterproxymodel.cpp

#include "basesortfilterproxymodel.h"


BaseSortFilterProxyModel::BaseSortFilterProxyModel(QObject *parent) : QSortFilterProxyModel(parent)
{

}

QVariant BaseSortFilterProxyModel::headerData(int section, Qt::Orientation orientation, int role)
{
    // переопределение сортировки нумерации строк
    if (role == Qt::DisplayRole){
        if (orientation == Qt::Vertical || section == 0){
            return section + 1;
        }
    }
    return BaseSortFilterProxyModel::headerData(section, orientation, role);
}

Заменил везде QSortFilterProxyModel на BaseSortFilterProxyModel, ничего не меняется. Наверное если сходу не понятно, то бесполезно и пытаться разобраться? Или есть какие-то доки и мануалы которые можно почитать.

Пример смотрели? Там всё более-менее понятно.

M

Пример смотрели? Там всё более-менее понятно.

Ну с сортировкой вертикальных заголовков разобрался теперь :)

Фильтры смотрю.

AC

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

#ifndef BASESORTFILTERPROXYMODEL_H
#define BASESORTFILTERPROXYMODEL_H

#include <QObject>
#include <QDebug>
#include <QDate>
#include <QDateTime>
#include <QSortFilterProxyModel>

class BaseSortFilterProxyModel : public QSortFilterProxyModel
{
    Q_OBJECT
public:
    BaseSortFilterProxyModel(QObject *parent = nullptr);

    virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const override;

protected:
    bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
};

#endif // BASESORTFILTERPROXYMODEL_H

Вам нужно разобраться с методом bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
Лучше всего создать проект-пример в котором запустить отладку проекта и разобраться.
1. Документация
2. Путь к примеру из самого QT Creator (у меня linux) - Qt/Examples/Qt-5.14.2/widgets/itemviews/customsortfiltermodel

Не сдавайтесь и будьте упрямее ...

Сложновато для меня, не понимаю что надо делать и что происходит. Наверное мне ещё рано.

... перечитайте ещё раз, если не поймёте запустите отладку проекта-примера и разберитесь в простом примере, а уж потом подумайте как применить к вашему проекту.

M

Итого, пришёл с одной проблемой, обнаружилось ещё несколько. Все были решены :) Но это я ещё остальной код не показывал. Понимаю что не хватает пару лет поработать джуном где-нибудь. Надо будет вам дать отревьюить как закончу.

Сделал изменения в теле топика.

Теперь ещё надо все миллион сигналов переписать по-новому.

Comments

Only authorized users can post comments.
Please, Log in or Sign up
How to become an author?

Contribute to the evolution of the EVILEG community.

Learn how to become a site author.

Learn it
Donate

Good day, Dear Users!!!

I am Evgenii Legotckoi, developer of EVILEG. And it is my hobby project, which helps to learn programming another programmers and developers

If the site helped you, and you want also support the development of the site, than you can donate by following ways

PayPalYandex.Money
Timeweb

Let me recommend you the excellent hosting on which EVILEG is located.

For many years, Timeweb has been proving his stability.

For projects on Django I recommend VDS hosting

View Hosting
R

C++ - Test 002. Constants

  • Result:75points,
  • Rating points2
R

C++ - Test 001. The first program and data types

  • Result:73points,
  • Rating points1
MS

C++ - Test 005. Structures and Classes

  • Result:75points,
  • Rating points2
Last comments
R

Qt WinAPI - Lesson 001. How to collect all DLL, which used in Qt project?

Вы меня не совсем правильно поняли, но все равно спасибо, принял все к сведению. Все сделал как вы сказали, все отлично работает, еще раз огромнейшее спасибо) Разве что только что были опять про…

Qt WinAPI - Lesson 001. How to collect all DLL, which used in Qt project?

Стоило перед использованием что ли инструкцию прочитать https://www.cyberforum.ru/blogs/131347/blog2457.html "После сборки при запуске требовались dll," Ясное дело стоило задепло…
R
R

Qt WinAPI - Lesson 001. How to collect all DLL, which used in Qt project?

Да, собралось. После сборки при запуске требовались dll, перекинул всю папки bin, plugins(не знаю как можно было сделать более умно). Как я понял в первой строке путь к екзешнику вставляю, втор…

Android. Java vs Qt QML - Tutorial 000. Enable Material Design

Это актуально для изменения цвета. В файле qtquickcontrols2.conf переменная Primary должна влиять на цвет приложения соответственно и цвет ApplicationBar должен поменяться. Но у status bar вроде…
Now discuss on the forum
A

Как в Qt в qmenu добавить scrollarea

Добрый день. Мое имя Кристина. Познакомлюсь с другом для встречи. Приеду к тебе в гости или встримся у меня. Живу близко. Мой адрес

Qt C++ и Python

Красиво/некрасиво - это скорее моё личное отношение. Если есть возможность ограничить количество интсрументов, то лучше ограничить. Но не зацикливайтесь на этом. Если у вас есть скрипты Py…

Qt + OpenGL glDeleteVertexArrays

Я не уверен, поскольку с OpenGL очень мало работал. Но может быть OpenGL контекст виджета нужно переинициализовывать. И ещё виджет стоит удалять через метод deleteLater() а не п…

QWebEngineView не запускается если к ПК подключено несколько мониторов

Ну я имел ввиду посмотреть на другом ПК с другой графикой и парой мониторов. Как моей программе назначить использовать определенный граф. адаптер? Вот тут понятия не имею.

Счечик производительности сети

Хорошо. После работы сегодня гляну ваш код внимательно.
About
Services
© EVILEG 2015-2020
Recommend hosting TIMEWEB