М
June 26, 2020, 7:27 p.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

  1. #include <QApplication>
  2. #include <QtWidgets>
  3.  
  4. class NameYearFilterProxyModel : public QSortFilterProxyModel{
  5. Q_OBJECT
  6. QRegExp nameRegExp;
  7. QRegExp yearRegExp;
  8. public:
  9. explicit NameYearFilterProxyModel(QObject* parent= nullptr):
  10. QSortFilterProxyModel(parent)
  11. {
  12. nameRegExp.setCaseSensitivity(Qt::CaseInsensitive);
  13. yearRegExp.setCaseSensitivity(Qt::CaseInsensitive);
  14. yearRegExp.setPatternSyntax(QRegExp::RegExp);
  15. nameRegExp.setPatternSyntax(QRegExp::RegExp);
  16. }
  17.  
  18. bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override{
  19. QModelIndex nameIndex= sourceModel()->index(sourceRow, 0, sourceParent);
  20. QModelIndex yearIndex= sourceModel()->index(sourceRow, 1, sourceParent);
  21.  
  22. QString name= sourceModel()->data(nameIndex).toString();
  23. QString year= sourceModel()->data(yearIndex).toString();
  24.  
  25. return (
  26.  
  27. name.contains(nameRegExp) && year.contains(yearRegExp)
  28.  
  29. );
  30. }
  31. public slots:
  32. void setNameFilter(const QString& regExp){
  33. nameRegExp.setPattern(regExp);
  34. invalidateFilter();
  35. }
  36. void setYearFilter(const QString& regExp){
  37. yearRegExp.setPattern(regExp);
  38. invalidateFilter();
  39. }
  40. };

custom_sort_filter_proxy_model.cpp

  1. #include "custom_sort_filter_proxy_model.h"
  2.  
  3. #include <QApplication>
  4.  
  5. int main(int argc, char *argv[])
  6. {
  7. QApplication a(argc, argv);
  8. //set up GUI
  9. QWidget w;
  10. w.setWindowTitle("Custom SortFilterProxyModel");
  11.  
  12. QVBoxLayout layout(&w);
  13. QHBoxLayout hLayout;
  14. QLineEdit lineEditName;
  15. QLineEdit lineEditYear;
  16.  
  17. lineEditName.setPlaceholderText("name filter");
  18. lineEditYear.setPlaceholderText("year filter");
  19. lineEditYear.setValidator(new QRegExpValidator(QRegExp("[0-9]*")));
  20. lineEditYear.setMaxLength(4);
  21. hLayout.addWidget(&lineEditName);
  22. hLayout.addWidget(&lineEditYear);
  23.  
  24. QTableView tableView;
  25. layout.addLayout(&hLayout);
  26. layout.addWidget(&tableView);
  27.  
  28. //set up models
  29. QStandardItemModel sourceModel;
  30. NameYearFilterProxyModel filterModel;;
  31. filterModel.setSourceModel(&sourceModel);
  32. tableView.setModel(&filterModel);
  33.  
  34. QObject::connect(&lineEditName, &QLineEdit::textChanged,
  35. &filterModel, &NameYearFilterProxyModel::setNameFilter);
  36. QObject::connect(&lineEditYear, &QLineEdit::textChanged,
  37. &filterModel, &NameYearFilterProxyModel::setYearFilter);
  38.  
  39.  
  40. QVector<QString> names{"Danny", "Christine", "Lars",
  41. "Roberto", "Maria"};
  42. for(int i=0; i<100; i++){
  43. QList<QStandardItem*> row;
  44. row.append(new QStandardItem(names[i%names.size()]));
  45. row.append(new QStandardItem(QString::number((i%9)+1980)));
  46. sourceModel.appendRow(row);
  47. }
  48. w.show();
  49. return a.exec();
  50. }

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

3

Do you like it? Share on social networks!

21
IscanderChe
  • June 26, 2020, 7:40 p.m.

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

    М
    • June 26, 2020, 8:07 p.m.
    • (edited)

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

    1. // получаю данные
    2. QSqlQuery query;
    3. QString str;
    4. str=("SELECT bla-bla-bla FROM bla "
    5. "LEFT JOIN bla "
    6. "ON bla-bla-bla.. ");
    7. query.exec(str);
    8.  
    9. model = new QSqlQueryModel;
    10. model->setQuery(query);
    11.  
    12. // для фильтрации по колонкам
    13. proxyOneModel->setSourceModel(model);
    14. proxyTwoModel->setSourceModel(proxyOneModel);
    15. proxyThreeModel->setSourceModel(proxyTwoModel);
    16. proxyFourModel->setSourceModel(proxyThreeModel);
    17. proxyFiveModel->setSourceModel(proxyFourModel);
    18.  
    19. view->setModel(proxyFiveModel);
    20.  
    21. // Включаю сортировку
    22. view->setSortingEnabled(true);
    23.  

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

    1. void dataBrowser::onTableClicked(const QModelIndex &index)
    2. {
    3. if (index.isValid()) {
    4. int columnNumber = index.column();
    5. int rowNumber = index.row();
    6.  
    7. if (columnNumber==2)
    8. {
    9. QModelIndex i = model->index(rowNumber, 0); // The first column data.
    10. emit setIdFromMain(i.data().toInt()); // по id записи вытаскиваю запросом строчку
    11. //в другое место, то есть тут получаю не id строчки которую вижу в таблице
    12. //а id который соответствует первоначальной сортировке по умолчанию
    13. }
    14. }
    15. }
    1. connect(view, SIGNAL(clicked(const QModelIndex &)), this, SLOT(onTableClicked(const QModelIndex &)));

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

      AC
      • June 26, 2020, 8:30 p.m.
      • The answer was marked as a solution.

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

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

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

        М
        • June 26, 2020, 8:35 p.m.

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

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

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

          AC
          • June 26, 2020, 8:45 p.m.
          • (edited)

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

          1. QSqlQueryModel *model = new QSqlQueryModel;
          2. QSortFilterProxyModel *proxy = new QSortFilterProxyModel(this);
          3. proxy->setSourceModel(model);
          4. ui->tableView->setModel(proxy);

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

            М
            • June 26, 2020, 8:49 p.m.

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

              М
              • June 26, 2020, 9:32 p.m.

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

                М
                • June 26, 2020, 9:41 p.m.

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

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

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

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

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

                  AC
                  • June 26, 2020, 9:43 p.m.

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

                    М
                    • June 26, 2020, 9:44 p.m.

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

                      IscanderChe
                      • June 26, 2020, 9:49 p.m.

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

                        М
                        • June 26, 2020, 10:01 p.m.

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

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

                        1. proxyTimeModel->setFilterKeyColumn(4);
                          IscanderChe
                          • June 26, 2020, 10:08 p.m.

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

                            М
                            • June 26, 2020, 10:11 p.m.

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

                              AC
                              • June 26, 2020, 10:18 p.m.
                              • (edited)

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

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

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

                              1. #include "mainwindow.h"
                              2. #include "ui_mainwindow.h"
                              3.  
                              4. static const int COLUMN_ID = 0; // ------- !!!!!!!!!!!!!!!!!
                              5. static const int COLUMN_NAME = 1; // ------- !!!!!!!!!!!!!!!!!
                              6.  
                              7. MainWindow::MainWindow(QWidget *parent)
                              8. : QMainWindow(parent)
                              9. , ui(new Ui::MainWindow)
                              10. {
                              11.  
                              12. }

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

                              1. connect(viewWindow, SIGNAL(triggered()), this, SLOT(show())); // старый синтаксис
                              2. connect(viewWindow, &QAction::triggered, this, &NameClass::show); // новый синтаксис
                                AC
                                • June 26, 2020, 10:33 p.m.

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

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

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

                                1. QVariant BaseSortFilterProxyModel::headerData(int section, Qt::Orientation orientation, int role) const
                                2. {
                                3. // переопределение сортировки нумерации строк
                                4. if (role == Qt::DisplayRole){
                                5. if (orientation == Qt::Vertical || section == 0){
                                6. return section + 1;
                                7. }
                                8. }
                                9. return QSortFilterProxyModel::headerData(section, orientation, role);
                                10. }
                                  М
                                  • June 27, 2020, 2:48 p.m.

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

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

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

                                  basesortfilterproxymodel.h

                                  1. #include <QSortFilterProxyModel>
                                  2. #include <QVariant>
                                  3.  
                                  4. class BaseSortFilterProxyModel : public QSortFilterProxyModel
                                  5. {
                                  6. public:
                                  7. BaseSortFilterProxyModel(QObject *parent = nullptr);
                                  8. QVariant headerData(int section, Qt::Orientation orientation, int role);
                                  9. };

                                  basesortfilterproxymodel.cpp

                                  1. #include "basesortfilterproxymodel.h"
                                  2.  
                                  3.  
                                  4. BaseSortFilterProxyModel::BaseSortFilterProxyModel(QObject *parent) : QSortFilterProxyModel(parent)
                                  5. {
                                  6.  
                                  7. }
                                  8.  
                                  9. QVariant BaseSortFilterProxyModel::headerData(int section, Qt::Orientation orientation, int role)
                                  10. {
                                  11. // переопределение сортировки нумерации строк
                                  12. if (role == Qt::DisplayRole){
                                  13. if (orientation == Qt::Vertical || section == 0){
                                  14. return section + 1;
                                  15. }
                                  16. }
                                  17. return BaseSortFilterProxyModel::headerData(section, orientation, role);
                                  18. }
                                  19.  

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

                                    IscanderChe
                                    • June 27, 2020, 3:03 p.m.

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

                                      М
                                      • June 27, 2020, 4:25 p.m.
                                      • (edited)

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

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

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

                                        AC
                                        • June 27, 2020, 5:06 p.m.

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

                                        1. #ifndef BASESORTFILTERPROXYMODEL_H
                                        2. #define BASESORTFILTERPROXYMODEL_H
                                        3.  
                                        4. #include <QObject>
                                        5. #include <QDebug>
                                        6. #include <QDate>
                                        7. #include <QDateTime>
                                        8. #include <QSortFilterProxyModel>
                                        9.  
                                        10. class BaseSortFilterProxyModel : public QSortFilterProxyModel
                                        11. {
                                        12. Q_OBJECT
                                        13. public:
                                        14. BaseSortFilterProxyModel(QObject *parent = nullptr);
                                        15.  
                                        16. virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
                                        17.  
                                        18. protected:
                                        19. bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
                                        20. };
                                        21.  
                                        22. #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

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

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

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

                                          М
                                          • June 27, 2020, 11:41 p.m.

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

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

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

                                            Comments

                                            Only authorized users can post comments.
                                            Please, Log in or Sign up
                                            • Last comments
                                            • AK
                                              April 1, 2025, 11:41 a.m.
                                              Добрый день. В данный момент работаю над проектом, где необходимо выводить звук из программы в определенное аудиоустройство (колонки, наушники, виртуальный кабель и т.д). Пишу на Qt5.12.12 поско…
                                            • Evgenii Legotckoi
                                              March 9, 2025, 9:02 p.m.
                                              К сожалению, я этого подсказать не могу, поскольку у меня нет необходимости в обходе блокировок и т.д. Поэтому я и не задавался решением этой проблемы. Ну выглядит так, что вам действитель…
                                            • VP
                                              March 9, 2025, 4:14 p.m.
                                              Здравствуйте! Я устанавливал Qt6 из исходников а также Qt Creator по отдельности. Все компоненты, связанные с разработкой для Android, установлены. Кроме одного... Когда пытаюсь скомпилиров…
                                            • ИМ
                                              Nov. 22, 2024, 9:51 p.m.
                                              Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
                                            • Evgenii Legotckoi
                                              Oct. 31, 2024, 11:37 p.m.
                                              Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup