ДК
Джон Кофи17 июня 2020 г. 8:04

разместить LineEdit под каждым столбом QTableView

Привет. Хочу сделать делегат от QTableView, в котором разместить QLineEdit под каждым столбцом, но в QTableView! Вводим текст в QLineEdit и он через 0,5 сек делает фильтр по введенному шаблону для этого столбца. Вобщем-то сделать всё получилось, только вот как разместить QLineEdit под каждым столбцом я не знаю. Прошу подсказать. ПС: пока разместил его просто поверх HorizontalHeaderLabels, как на скрине. Проект прилагаю .zip

#ifndef FILTERTABLEVIEW_H
#define FILTERTABLEVIEW_H

#include <QTableView>
#include <QTimer>
#include <QLineEdit>
#include <QHBoxLayout>
#include <QHeaderView>
#include <QRegExpValidator>
#include <QAbstractItemModel>
#include <QSortFilterProxyModel>


class FilterTableView : public QTableView
{
    Q_OBJECT
public:
    explicit FilterTableView(QWidget *parent = nullptr);

    void setRegExpForColumn(QMap<int, QString> regexpColumns);
    void setEditColumns(QVector<int> editColumns);

    void setModel(QAbstractItemModel *model) Q_DECL_OVERRIDE;

private:
    void setFilter(const QString text, const int column);
    QWidget *createLineEditForColumn(const int column);
    void filterLayoutAddWidgets();


    QHBoxLayout *_filterLayout;
    QAbstractItemModel *_model;
    QMap<int, QString> _regexpColumns;
    QVector<int> _editColumns;
    QTimer *_timer;
signals:
    void filterTViewTextChanged(const int column, const QString text);
};

#endif // FILTERTABLEVIEW_H

#include "filtertableview.h"


FilterTableView::FilterTableView(QWidget *parent) :
    QTableView(parent)
{
    _timer = new QTimer(this);
    _filterLayout = new QHBoxLayout;
    horizontalHeader()->setLayout(_filterLayout);
    horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeMode::Stretch);
}


void FilterTableView::filterLayoutAddWidgets()
{
    if(!_editColumns.isEmpty())
        for(int i(0); i < _editColumns.size(); ++i)
            _filterLayout->addWidget(createLineEditForColumn(_editColumns[i]));
    else
        for(int i(0); i < _model->columnCount(); ++i)
            _filterLayout->addWidget(createLineEditForColumn(i));
}

QWidget *FilterTableView::createLineEditForColumn(const int column)
{
    QLineEdit *widget = new QLineEdit();
    if(_regexpColumns.contains(column))
        widget->setValidator(new QRegExpValidator(QRegExp(_regexpColumns.value(column))));

    connect(widget, &QLineEdit::textChanged, [this, column](const QString text)
    {
        _timer->start(500);
        connect(_timer, &QTimer::timeout, [this, column, text](){
            emit filterTViewTextChanged(column, text);});
    });
    return widget;
}

void FilterTableView::setRegExpForColumn(QMap<int, QString> regexpColumns)
{
    _regexpColumns = regexpColumns;
}

void FilterTableView::setEditColumns(QVector<int> editColumns)
{
    _editColumns = editColumns;
}

void FilterTableView::setModel(QAbstractItemModel *model)
{
    _model = model;
    QTableView::setModel(model);
    filterLayoutAddWidgets();
}


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

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    _tv = new FilterTableView();

    _proxy = new QSortFilterProxyModel;
    _model = new QStandardItemModel(_tv);
    _model->setHorizontalHeaderLabels(QStringList() << "Column 1" << "Column 2" << "Column 3");
    for (int i = 0; i < 4; ++i) {
        QList<QStandardItem *> items;
        items.append(new QStandardItem(QString("Row %1").arg(i)));  // Первая колонка
        items.append(new QStandardItem("Item 2"));                  // Вторая колонка
        items.append(new QStandardItem("Item 3"));                  // Третья колонка
        _model->appendRow(items);
    }
    _proxy->setSourceModel(_model);
    _tv->setModel(_proxy);
    ui->verticalLayout->addWidget(_tv);

    connect(_tv, &FilterTableView::filterTViewTextChanged, [this](const int column, const QString text){
        setFilter(column, text);
    });
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::setFilter(const int column, const QString pattern)
{
    _proxy->setFilterKeyColumn(column);
    _proxy->setFilterCaseSensitivity(Qt::CaseInsensitive);
    _proxy->setFilterFixedString(pattern);
}


Ну и замечания по коду приму во внимание.

Filter.zip Filter.zip

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

Вам это нравится? Поделитесь в социальных сетях!

12
Evgenii Legotckoi
  • 17 июня 2020 г. 8:14

Добрый день.

QHeaderView имеет метод setItemDelegateForColumn . Думаю, что вам нужно написать делегат, в котором будет QLineEdit. И устанавливать этот делегат в колонки QHeaderView.

Пример написания делегата есть в этой статье Qt/C++ - Урок 091. Как написать кастомный делегат управляющий подсветкой строки в таблице

Единственное, вам нужно будет разобраться с тем, чтобы добавить туда QLineEdit... Возможно, через метод createEditor (кажется так называется), то есть переопределить его.

    ДК
    • 17 июня 2020 г. 10:31

    не получается. Может быть из-за этого

    Note: Each header renders the data for each section itself, and does not rely on a delegate. As a result, calling a header's setItemDelegate() function will have no effect.

      Evgenii Legotckoi
      • 17 июня 2020 г. 10:32

      наверное )) Этого я не заметил, сорян

        ДК
        • 17 июня 2020 г. 10:35
        • (ред.)

        какие способы вообще приемлемы для отображения строки ввода под названием столбца?
        Пример, как я хочу сделать (нажать кнопку фильтра).

          ДК
          • 17 июня 2020 г. 10:40

          headerView переопределить может быть..

            ДК
            • 17 июня 2020 г. 11:39

            делаю проверку в делегате такую

            if(!_filterLayout->setAlignment(horizontalHeader(), Qt::AlignBottom))
                    qDebug() << "FALSE";
            

            всегда FALSE. никуда ничего в QHeaderView не хочет ни добавляться, ни перемещаться. Жестянка какая-то.

              Evgenii Legotckoi
              • 18 июня 2020 г. 2:35

              Понял, что вы хотите сделать... Надо подумать.

                ДК
                • 18 июня 2020 г. 3:26

                я вот , что вчера нашел))
                уже сделал, но что-то не так, хотя, заметно лучше уже)

                  ДК
                  • 18 июня 2020 г. 3:28
                  • (ред.)

                  а еще решение подсказали:
                  присосаться к

                  signal void QHeaderView::sectionDoubleClicked(int logicalindex)
                  

                  по которому открывать лайнэдит прямо под курсором. Но мне может быть такое не разрешат сделать.

                    ДК
                    • 18 июня 2020 г. 5:55
                    • (ред.)

                    сделал. Думаю, по этой задаче можно и статейку написать для коллекции, тк решений в сети нет. Получилось в два делегата, тк мне необходима была задержка фильтрации по столбцам.

                    class FilterHeader : public QHeaderView
                    {
                        Q_OBJECT
                    public:
                        explicit FilterHeader(Qt::Orientation orientation, QWidget *parent = nullptr);
                    
                        void setFilterBoxes(const int columnCount);
                    
                        QSize sizeHint() const          Q_DECL_OVERRIDE;
                    
                    protected:
                        void updateGeometries()         Q_DECL_OVERRIDE;
                    
                    private:
                        void adjustPositions();
                    
                    private:
                        QVector<QLineEdit*> _editors;
                        int _padding;
                        QTimer *_timer;
                    
                    signals:
                        void filterTViewTextChanged(const int column, const QString text);
                    };
                    
                    #include "filterheader.h"
                    
                    
                    FilterHeader::FilterHeader(Qt::Orientation orientation, QWidget *parent):
                        QHeaderView(orientation, parent),
                        _editors({}),
                        _padding(4)
                    {
                        _timer = new QTimer(this);
                        setStretchLastSection(true);
                        setSectionResizeMode(QHeaderView::Stretch);
                        setDefaultAlignment(Qt::AlignLeft | Qt::AlignCenter);
                        setSortIndicatorShown(false);
                        connect(this, &FilterHeader::sectionResized, this, &FilterHeader::adjustPositions);
                        connect(horizontalScrollBar(), &QScrollBar::valueChanged, this, &FilterHeader::adjustPositions);
                    }
                    
                    void FilterHeader::setFilterBoxes(const int columnCount)
                    {
                        QMap<int, QString> regexpColumns = static_cast<FilterTableView*>(parent())->getRegExpColumns();
                        for(int i(0); i < columnCount; ++i)
                        {
                    //Если массив исключений содержит столбцы, котрым не нужно добавлять QLineEdit
                            if(static_cast<FilterTableView*>(parent())->getExpcludeColumns().contains(i))
                                break;
                            QLineEdit *le = new QLineEdit(this);
                            le->setPlaceholderText(QString("Фильтр"));
                    //Если массив проверки валидации для добавляемого QLineEdit столбца содержит правила валидации
                            if(regexpColumns.contains(i))
                                le->setValidator(new QRegExpValidator(QRegExp(regexpColumns.value(i))));
                    
                            _editors.append(le);
                    //Данный коннект делает задержку фильтрации на 1 секунду
                            connect(le, &QLineEdit::textChanged, [this, i](const QString text)
                            {
                                _timer->start(1000);
                                connect(_timer, &QTimer::timeout, [this, i, text](){
                                    emit filterTViewTextChanged(i, text);});
                            });
                        }
                        adjustPositions();
                    }
                    
                    QSize FilterHeader::sizeHint() const
                    {
                        QSize size = QHeaderView::sizeHint();
                        if(!_editors.isEmpty())
                        {
                            const int height = _editors[0]->sizeHint().height();
                            size.setHeight(size.height() + height + _padding);
                        }
                        return size;
                    }
                    
                    void FilterHeader::updateGeometries()
                    {
                        if(!_editors.isEmpty())
                        {
                            const int height = _editors[0]->sizeHint().height();
                            setViewportMargins(0, 0, 0, height + _padding);
                        }
                        else
                            setViewportMargins(0, 0, 0, 0);
                        QHeaderView::updateGeometries();
                        adjustPositions();
                    }
                    
                    void FilterHeader::adjustPositions()
                    {
                    //Метод изменения позиции QLineEdit
                        int column = 0;
                        foreach(QLineEdit *le, _editors)
                        {
                            const int height = _editors[column]->sizeHint().height() - 2;
                            le->move(sectionPosition(column) - offset(), height + (_padding/5));
                            le->resize(sectionSize(column), height);
                            ++column;
                        }
                    }
                    
                    class FilterHeader;
                    
                    class FilterTableView : public QTableView
                    {
                        Q_OBJECT
                    public:
                        explicit FilterTableView(QWidget *parent = nullptr);
                    
                        void setRegExpForColumn(QMap<int, QString> regexpColumns);
                        void setEditColumns(QVector<int> editColumns);
                    
                        QMap<int, QString>  getRegExpColumns();
                        QVector<int>        getExpcludeColumns();
                    
                        void setModel(QAbstractItemModel *model) Q_DECL_OVERRIDE;
                    
                        QMap<int, QString> *getGetRegEpColumns() const;
                        void setGetRegEpColumns(QMap<int, QString> *value);
                    
                    private:
                        QVBoxLayout *_filterLayout;
                        QAbstractItemModel *_model;
                        QMap<int, QString> _regExpColumns;
                        QVector<int> _expcludeColumns;
                        FilterHeader *_header;
                    
                    signals:
                        void filterTViewTextChanged(const int column, const QString text);
                    };
                    
                    #include "filtertableview.h"
                    
                    
                    FilterTableView::FilterTableView(QWidget *parent) :
                        QTableView(parent)
                    {
                        _header = new FilterHeader(Qt::Horizontal, this);
                        setHorizontalHeader(_header);
                        _filterLayout = new QVBoxLayout(this);
                        horizontalHeader()->setLayout(_filterLayout);
                        horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeMode::Stretch);
                    
                        connect(_header, &FilterHeader::filterTViewTextChanged, this, &FilterTableView::filterTViewTextChanged);
                    }
                    
                    
                    void FilterTableView::setModel(QAbstractItemModel *model)
                    {
                        _model = model;
                        _header->setFilterBoxes(_model->columnCount());
                        QTableView::setModel(model);
                    }
                    
                    void FilterTableView::setRegExpForColumn(QMap<int, QString> regexpColumns)
                    {
                        _regExpColumns = regexpColumns;
                    }
                    
                    void FilterTableView::setEditColumns(QVector<int> editColumns)
                    {
                        _expcludeColumns = editColumns;
                    }
                    
                    QMap<int, QString> FilterTableView::getRegExpColumns()
                    {
                        return _regExpColumns;
                    }
                    
                    QVector<int> FilterTableView::getExpcludeColumns()
                    {
                        return _expcludeColumns;
                    }
                    
                    MainWindow::MainWindow(QWidget *parent)
                        : QMainWindow(parent)
                        , ui(new Ui::MainWindow)
                    {
                        ui->setupUi(this);
                        createModel();
                        _tv = new FilterTableView();
                        _tv->setModel(_proxy);
                    
                        ui->verticalLayout->addWidget(_tv);
                    
                        connect(_tv, &FilterTableView::filterTViewTextChanged, [this](const int column, const QString text){
                            setFilter(column, text);
                        });
                    }
                    
                    MainWindow::~MainWindow()
                    {
                        delete ui;
                    }
                    
                    void MainWindow::setFilter(const int column, const QString pattern)
                    {
                        _proxy->setFilterKeyColumn(column);
                        _proxy->setFilterCaseSensitivity(Qt::CaseInsensitive);
                        _proxy->setFilterFixedString(pattern);
                    }
                    
                    void MainWindow::createModel()
                    {
                        _model = new QStandardItemModel(ui->_tableView);
                        _model->setHorizontalHeaderLabels(QStringList() << "Column 1" << "Column 2" << "Column 3");
                        for (int i = 0; i < 4; ++i) {
                            QList<QStandardItem *> items;
                            items.append(new QStandardItem(QString("Row %1").arg(i)));  // Первая колонка
                            items.append(new QStandardItem("Item 2"));                  // Вторая колонка
                            items.append(new QStandardItem("Item 3"));                  // Третья колонка
                            _model->appendRow(items);
                        }
                        _proxy = new QSortFilterProxyModel;
                        _proxy->setSourceModel(_model);
                    }
                    

                      Evgenii Legotckoi
                      • 18 июня 2020 г. 5:56

                      Думаю, что сообщество будет радо увидеть подобную статью. Дерзайте ))

                        ВП
                        • 1 января 2023 г. 7:15
                        • (ред.)

                        Наидобрейший человек! Спасибо! Код действительно очень помог сообществу. Его до сих под не сыскать на просторах чего то подобного на C++. Сделал несколько оптимизаций:

                        (1). Вернуть сортировку по нажатию на столбец можно в конструкторе FilterTableView, добавив:

                        _header->setSectionsClickable(true);
                        _header->setHighlightSections(true);
                        

                        Функция

                        setSortingEnabled(true)
                        

                        тут не поможет

                        (2). Чтобы не дергать итоговую проксимодель ненужными сигналами, то на выходе мы создаем единственный сигнал об изменениях которые были сделаны во всех столбцах с задержской в 1 секунду.
                        В конструкторе "FilterHeader" добавляем

                        timer->setSingleShot(true);
                        

                        Переписываем FilterHeader::setFilterBoxes, ну и меняем сигнал на нужный тип данных в заголовочном файле

                        void FilterHeader::setFilterBoxes(const int columnCount)
                        {
                            QMap<int, QString> regexpColumns = static_cast<FilterTableView*>(parent())->getRegExpColumns();
                            for (int i=0; i < columnCount; ++i)
                            {
                                QLineEdit *le = new QLineEdit(this);
                                le->setPlaceholderText(QString("Фильтр"));
                                //Если массив исключений содержит столбцы, котрым не нужно добавлять QLineEdit
                                if (static_cast<FilterTableView*>(parent())->getExpcludeColumns().contains(i))
                                {
                                    le->hide();
                                }
                                //Если массив проверки валидации для добавляемого QLineEdit столбца содержит правила валидации
                                if(regexpColumns.contains(i))
                                    le->setValidator(new QRegularExpressionValidator(QRegularExpression(regexpColumns.value(i))));
                        
                                _editors.append(le);
                                //Данный коннект делает задержку фильтрации на 1 секунду
                                connect(le, &QLineEdit::textChanged, [this]()
                                {
                                    if (timer->isActive())
                                        timer->setInterval(100);
                                    else
                                        timer->start(100);
                                });
                            }
                            connect(timer, &QTimer::timeout, [this]()
                            {
                                QStringList editors_values;
                                for (int i=0; i<_editors.count(); i++)
                                {
                                    editors_values.append(_editors[i]->text());
                                }
                                emit textReady(editors_values);
                            });
                            adjustPositions();
                        }
                        

                        (3). Возвращаем подвижность столбцам в конструкторе FilterTableView параметром

                        horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeMode::Interactive);
                        

                          Комментарии

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

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

                          • Результат:16баллов,
                          • Очки рейтинга-10
                          B

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

                          • Результат:46баллов,
                          • Очки рейтинга-6
                          FL

                          C++ - Тест 006. Перечисления

                          • Результат:80баллов,
                          • Очки рейтинга4
                          Последние комментарии
                          k
                          kmssr8 февраля 2024 г. 15:43
                          Qt Linux - Урок 001. Автозапуск Qt приложения под Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
                          АК
                          Анатолий Кононенко4 февраля 2024 г. 22:50
                          Qt WinAPI - Урок 007. Работаем с ICMP Ping в Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
                          EVA
                          EVA25 декабря 2023 г. 7:30
                          Boost - статическая линковка в CMake проекте под Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
                          J
                          JonnyJo25 декабря 2023 г. 5:38
                          Boost - статическая линковка в CMake проекте под Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
                          G
                          Gvozdik18 декабря 2023 г. 18:01
                          Qt/C++ - Урок 056. Подключение библиотеки Boost в Qt для компиляторов MinGW и MSVC Для решения твой проблемы добавь в файл .pro строчку "LIBS += -lws2_32" она решит проблему , лично мне помогло.
                          Сейчас обсуждают на форуме
                          P
                          Pisych27 февраля 2023 г. 1:04
                          Как получить в массив значения из связанной модели? Спасибо, разобрался:))
                          AC
                          Alexandru Codreanu19 января 2024 г. 8:57
                          QML Обнулить значения SpinBox Доброго времени суток, не могу разобраться с обнулением значение SpinBox находящего в делегате. import QtQuickimport QtQuick.ControlsWindow { width: 640 height: 480 visible: tr…
                          BlinCT
                          BlinCT27 декабря 2023 г. 5:57
                          Растягивать Image на парент по высоте Ну и само собою дял включения scrollbar надо чтобы был Flickable. Так что выходит как то так Flickable{ id: root anchors.fill: parent clip: true property url linkFile p…
                          Дмитрий
                          Дмитрий10 января 2024 г. 1:18
                          Qt Creator загружает всю оперативную память Проблема решена. Удалось разобраться с помощью утилиты strace. Запустил ее: strace ./qtcreator Начал выводиться весь лог работы креатора. В один момент он начал считывать фай…
                          Evgenii Legotckoi
                          Evgenii Legotckoi12 декабря 2023 г. 3:48
                          Побуквенное сравнение двух строк Добрый день. Там случайно не высылается этот сигнал textChanged ещё и при форматировани текста? Если решиать в лоб, то можно просто отключать сигнал/слотовое соединение внутри слота и …

                          Следите за нами в социальных сетях