М
МаркГлен26 июня 2020 г. 9:27

Сортировка и фильтрация в 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();
}

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

Рекомендуем хостинг TIMEWEB
Рекомендуем хостинг TIMEWEB
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.
21

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

М

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

// получаю данные
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
  • 26 июня 2020 г. 10:30
  • Ответ был помечен как решение.

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

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

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

М

код я тут привёл 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

М

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

М

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

М

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

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

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

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

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

AC

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

М

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

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

М

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

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

proxyTimeModel->setFilterKeyColumn(4);

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

М

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

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);
}
М

Посмотрите в сторону кастомной модели от 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, ничего не меняется. Наверное если сходу не понятно, то бесполезно и пытаться разобраться? Или есть какие-то доки и мануалы которые можно почитать.

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

М

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

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

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

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

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

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

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

М

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

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

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

Комментарии

Только авторизованные пользователи могут публиковать комментарии.
Пожалуйста, авторизуйтесь или зарегистрируйтесь
Как стать автором?

Внесите вклад в развитие сообщества EVILEG.

Узнайте, как стать автором сайта.

Изучить
Donate

Добрый день, Дорогие Пользователи !!!

Я Евгений Легоцкой, разработчик EVILEG. И это мой хобби-проект, который помогает учиться программированию другим программистам и разработчикам

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

PayPalYandex.Money
Timeweb

Позвольте мне порекомендовать вам отличный хостинг, на котором расположен EVILEG.

В течение многих лет Timeweb доказывает свою стабильность.

Для проектов на Django рекомендую VDS хостинг

Посмотреть Хостинг
R

C++ - Тест 002. Константы

  • Результат:75баллов,
  • Очки рейтинга2
R

C++ - Тест 001. Первая программа и типы данных

  • Результат:73баллов,
  • Очки рейтинга1
MS

C++ - Тест 005. Структуры и Классы

  • Результат:75баллов,
  • Очки рейтинга2
Последние комментарии
R

Qt WinAPI - Урок 001. Как собрать все DLL, используемые в Qt-проекте?

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

Qt WinAPI - Урок 001. Как собрать все DLL, используемые в Qt-проекте?

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

Qt WinAPI - Урок 001. Как собрать все DLL, используемые в Qt-проекте?

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

Android. Java vs Qt QML - Урок 000. Включение Material Design

Это актуально для изменения цвета. В файле qtquickcontrols2.conf переменная Primary должна влиять на цвет приложения соответственно и цвет ApplicationBar должен поменяться. Но у status bar вроде…
Сейчас обсуждают на форуме
A

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

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

Qt C++ и Python

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

Qt + OpenGL glDeleteVertexArrays

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

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

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

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

Хорошо. После работы сегодня гляну ваш код внимательно.
О нас
Услуги
© EVILEG 2015-2020
Рекомендует хостинг TIMEWEB