Arrow
Arrow23. Juli 2018 05:04

QComboBox делегат для QTableView

Qt

Добрый день!
Подскажите пожалуйста как можно реализовать делегат в виде QComboBox для ячейки в QTableView.
Данные в сам QComboBox будут браться с отдельной таблицы в базе данных и id выбранной записи необходимо записать в основную таблицу.
Искал в сети пример, но везде реализуют только делегат с уже внесенными данными в конструкторе самого делегата.

Пробовал так:

#ifndef COMBODELEGATE_H
#define COMBODELEGATE_H

#include <QItemDelegate>
#include <QComboBox>

class QModelIndex;
class QWidget;
class QVariant;

class ComboBoxDelegate : public QItemDelegate
{
Q_OBJECT
public:
ComboBoxDelegate(QObject *parent = 0);

QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const;
void setEditorData(QWidget *editor, const QModelIndex &index) const;
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const;
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const;
};

#endif // COMBODELEGATE_H
Реализация:
#include "combodelegate.h"

#include <QWidget>
#include <QModelIndex>
#include <QAbstractItemModel>

ComboBoxDelegate::ComboBoxDelegate(QObject *parent)
:QItemDelegate(parent)
{

}

QWidget *ComboBoxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex &/* index */) const
{
QComboBox* editor = new QComboBox(parent);
return editor;
}

void ComboBoxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
QComboBox *comboBox = dynamic_cast<QComboBox*>(editor);
int value = index.data(Qt::EditRole).toInt();
comboBox->setCurrentIndex(value);
}

Делегат отображается в ячейке, так, но пустой:
ui->mainTableView->setItemDelegateForColumn(2, new ComboBoxDelegate(this));

Как в него запихнуть модель с данными и затем получать id выбранной записи не пойму.
Рекомендуємо хостинг TIMEWEB
Рекомендуємо хостинг TIMEWEB
Stabiles Hosting des sozialen Netzwerks EVILEG. Wir empfehlen VDS-Hosting für Django-Projekte.

Magst du es? In sozialen Netzwerken teilen!

14
Evgenii Legotckoi
  • 23. Juli 2018 05:33

У QComboBox есть метод setModel()

В методе setEditorData вам нужно каким-то образом создать модель с необходимыми данными, и поместить его в этот комбобокс, как я полагаю.
void ComboBoxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
    QComboBox *comboBox = dynamic_cast<QComboBox*>(editor);
    int value = index.data(Qt::EditRole).toInt();

    // Создать здесь модель
    // Думаю, что можно воспользоваться QStandardItemModel, или какая вам будет удобнее, если данные берутся из БД
    auto* yourModelChoices = new SomeTypeModel(comboBox); // Или ещё какой-то иной способ получения модели
    comboBox->setModel(yourModelChoices)
    comboBox->setCurrentIndex(value);
}

То есть то, как достать эту модель для комбобокса - уже зависит от более конретной логики вашего приложения... Возможно можете написать какую-нибудь SqlQueryModel или ещё что-то в этом роде.


    Arrow
    • 23. Juli 2018 06:19
    • (bearbeitet)
    Есть модель написанная под QComboBox:

    BaseComboModel::BaseComboModel( const QString &visualColumn, const QString &queryTail, QObject *parent,
    const QString &baseTable, const QString &baseColumn ) :
    QSqlQueryModel( parent ),
    mainTable(baseTable), mainColumn(baseColumn)
    {
    selectionColumn = visualColumn;
    selectionTable = queryTail.mid(0, queryTail.indexOf(" "));
    QSqlQuery query;
    query.prepare( QString( "SELECT %1.id, %2 FROM %3" ).arg( queryTail.split( ' ' ).first() ).arg( visualColumn ).arg( queryTail ) );
    query.exec();
    QSqlQueryModel::setQuery( query );
    }


    QVariant BaseComboModel::data(const QModelIndex &item, int role) const
    {
    QVariant result;

    if( item.row() == 0 )
    {
    switch( role )
    {
    case Qt::UserRole:
    result = 0;
    break;
    case Qt::DisplayRole:
    result = "(please select)";
    break;
    default:
    break;
    }
    }
    else
    {
    switch( role )
    {
    case Qt::UserRole:
    result = dataFromParent( item, Id );
    break;
    case Qt::DisplayRole:
    result = dataFromParent( item, Data );
    break;
    default:
    break;
    }
    }

    return result;
    }
    В делегате делаю так:
    void ComboBoxDelegate::setEditorData(QWidget *editor,
    const QModelIndex &index,
    const QString column,
    const QString table,
    const QString baseTable,
    const QString baseColumn) const
    {
    QComboBox *comboBox = dynamic_cast<QComboBox*>(editor);
    //int value = index.model()->data(index, Qt::EditRole).toUInt();
    int value = index.data(Qt::EditRole).toInt();

    BaseComboModel *model = new BaseComboModel(column, table, nullptr, baseTable, baseColumn);
    comboBox->setModel(model);
    comboBox->setCurrentIndex(value);
    }
    Использование:
    ComboBoxDelegate *equipdelegate = new ComboBoxDelegate();
    equipdelegate->setEditorData(new QComboBox(), QModelIndex(), Equipment_Name, Equipment_Table, Main_Table, "equipment");
    ui->mainTableView->setItemDelegateForColumn(2, equipdelegate);
    С QComboBox эта модель работает отлично, а здесь в делегате по прежнему пусто. Может я не правильно понял или что-то не так делаю?
      Arrow
      • 23. Juli 2018 07:24

      И можно еще один маленький вопрос: Как изменить значение в ячейке QTableView?

        Evgenii Legotckoi
        • 24. Juli 2018 02:45

        Думается мне, что вся проблема в том, что в

        equipdelegate->setEditorData(new QComboBox(), QModelIndex(), Equipment_Name, Equipment_Table, Main_Table, "equipment");

        Передаётся в качестве аргумента QModelIndex() - который по существу невалидный. То есть непонятно, что там будет и будет ли работать.
        Но скорее всего есть ещё что-то.
        Также вот это место нужно переписать так

        BaseComboModel::BaseComboModel( const QString &visualColumn, const QString &queryTail, QObject *parent,
                const QString &baseTable, const QString &baseColumn ) :
                QSqlQueryModel( parent ),
                mainTable(baseTable), mainColumn(baseColumn)
        {
            selectionColumn = visualColumn;
            selectionTable = queryTail.mid(0, queryTail.indexOf(" "));
            QSqlQueryModel::setQuery( QString( "SELECT %1.id, %2 FROM %3" ).arg( queryTail.split( ' ' ).first() ).arg( visualColumn ).arg( queryTail ));
        }

        QSqlQuery выполняется в методе setQuery
        Попробуйте для начала сделать какую-нибудь QStandardItemModel и установить её в делегате в методе setEditorData, чтобы посмотреть, работает ли там в принципе что-нибудь.


          Evgenii Legotckoi
          • 24. Juli 2018 02:48

          Вам нужно реализовать метод setData в вашей QSqlQueryModel. Эта модель, из-за того, что она использует сырые запросы, является Read-Only, поскольку невозможно определить логику модели заранее... Там может быть спойка нескольких таблиц с самой хитрой логикой.

          Поэтому нужно переопределить метод setData и самостоятельно написать сырые SQL запросы для изменения данных.
            Arrow
            • 24. Juli 2018 04:07
            • (bearbeitet)
            • Die Antwort wurde als Lösung markiert.
            Огромное спасибо за помощь!
            Решил уйти от QSqlQueryModel и реализовать все на QSqlQuery с использованием свойства Qt::UserRole из QCombobox.
            Если кому-то пригодится выкладываю рабочий вариант и пример использования (заголовочный не выкладываю там и так все ясно).
            Реализация делегата:
            #include "combodelegate.h"
            
            #include <QComboBox>
            #include <QSqlQuery>
            
            ComboBoxDelegate::ComboBoxDelegate(QObject *parent, const QString &visualColumn, const QString &queryTail)
                :QStyledItemDelegate(parent)
            {
                this->visualColumn = visualColumn;
                this->queryTail = queryTail;
            }
            
            QWidget *ComboBoxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex &/* index */) const
            {
                auto *editor = new QComboBox(parent);
                QSqlQuery query;
                query.prepare( QString( "SELECT %1.id, %2 FROM %3" ).arg( queryTail.split( ' ' ).first() ).arg( visualColumn ).arg( queryTail ) );
                query.exec();
            
                while (query.next()) {
                    // query.value(1).toString() - text
                    // query.value(0) - userData (id)
                    editor->addItem(query.value(1).toString(), query.value(0));
                }
                return editor;
            }
            
            void ComboBoxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
            {
                auto *comboBox = dynamic_cast<QComboBox*>(editor);
                QString currentText = index.data(Qt::EditRole).toString();
                int cbIndex = comboBox->findText(currentText);
                if (cbIndex >= 0)
                    comboBox->setCurrentIndex(cbIndex);
            }
            
            void ComboBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
            {
                if (auto *comboBox = dynamic_cast<QComboBox*>(editor)) {
                    QVariant modelData = comboBox->itemData(comboBox->currentIndex(), Qt::UserRole);    // id из Qt::UserRole
                    model->setData(index, modelData.toString(), Qt::EditRole);
                }
                else
                    QStyledItemDelegate::setModelData(editor, model, index);
            }
            
            void ComboBoxDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */) const
            {
                editor->setGeometry(option.rect);
            }
            

            Использование:
            mainModel->setRelation(2, QSqlRelation("table_name", "id", "column_name"));
            
            auto *eqCb = new ComboBoxDelegate(ui->mainTableView, "column_name", "table_name");
            ui->mainTableView->setItemDelegateForColumn(2, eqCb);

            Если у Вас есть какие-то поправки или улучшения, буду им рад (никакой код не совершенный).

              Arrow
              • 24. Juli 2018 08:01

              Только один вопрос пока не понятно как решить : измение значения в ячейке таблицы.

              Пользователь вводит данные в ячейку 1 и ячейку 2, а в третью нужно записать их сумму.
              Как изменить значение знаю,  а как отследить изменения в ячейках нет. Хочется это сделать без написания своего класса наследника.


                Evgenii Legotckoi
                • 24. Juli 2018 08:20

                Вообще, для отслеживания изменений используется сигнал dataChanged(const QModelIndex &, const QModelIndex &, const QVector<int> &), который передаёт индексы ячеек, где было изменение. Можете подключиться к этому сигналу и отслеживат, что и где изменилось. По изменению в первом и втором столбце делать изменения в третьем. При этом изменения втретьем столбце игнорировать

                  Arrow
                  • 24. Juli 2018 08:25

                  Понял. Спасибо! Чуть не перемудрил :)

                    Arrow
                    • 24. Juli 2018 09:20

                    Не выходит с dataChanged в QTableView он protected.

                      Evgenii Legotckoi
                      • 24. Juli 2018 09:26

                      Упс.. я перепутал.. сигнал у модели. Если у вас стандартная какая-нибудь модель, то должен вызываться, если наследованная, то возможно вам самостоятельно нужно будет прописать место, где вызывать этот сигнал.

                        Arrow
                        • 24. Juli 2018 09:29
                        Да я уже понял. Спасибо!

                        connect(ui->mainTableView->model(), &QAbstractItemModel::dataChanged, this, &TableForm::calculateTime);
                          Arrow
                          • 24. Juli 2018 09:32
                          Только не понимаю как из этого получить ячейку:

                          calculateTime(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles)
                            Arrow
                            • 24. Juli 2018 09:53
                            Все разобрался в
                            topLeft, bottomRigt

                            есть column и row.

                              Kommentare

                              Nur autorisierte Benutzer können Kommentare posten.
                              Bitte Anmelden oder Registrieren
                              Letzte Kommentare
                              A
                              ALO1ZE19. Oktober 2024 08:19
                              Fb3-Dateileser auf Qt Creator Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
                              ИМ
                              Игорь Максимов5. Oktober 2024 07:51
                              Django – Lektion 064. So schreiben Sie eine Python-Markdown-Erweiterung Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
                              d
                              dblas55. Juli 2024 11:02
                              QML - Lektion 016. SQLite-Datenbank und das Arbeiten damit in QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
                              k
                              kmssr8. Februar 2024 18:43
                              Qt Linux - Lektion 001. Autorun Qt-Anwendung unter Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
                              Qt WinAPI - Lektion 007. Arbeiten mit ICMP-Ping in Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
                              Jetzt im Forum diskutieren
                              J
                              JacobFib17. Oktober 2024 03:27
                              добавить qlineseries в функции Пользователь может получить любые разъяснения по интересующим вопросам, касающимся обработки его персональных данных, обратившись к Оператору с помощью электронной почты https://topdecorpro.ru…
                              JW
                              Jhon Wick1. Oktober 2024 15:52
                              Indian Food Restaurant In Columbus OH| Layla’s Kitchen Indian Restaurant If you're looking for a truly authentic https://www.laylaskitchenrestaurantohio.com/ , Layla’s Kitchen Indian Restaurant is your go-to destination. Located at 6152 Cleveland Ave, Colu…
                              КГ
                              Кирилл Гусарев27. September 2024 09:09
                              Не запускается программа на Qt: точка входа в процедуру не найдена в библиотеке DLL Написал программу на C++ Qt в Qt Creator, сбилдил Release с помощью MinGW 64-bit, бинарнику напихал dll-ки с помощью windeployqt.exe. При попытке запуска моей сбилженной программы выдаёт три оши…
                              F
                              Fynjy22. Juli 2024 04:15
                              при создании qml проекта Kits есть но недоступны для выбора Поставил Qt Creator 11.0.2. Qt 6.4.3 При создании проекта Qml не могу выбрать Kits, они все недоступны, хотя настроены и при создании обычного Qt Widget приложения их можно выбрать. В чем может …

                              Folgen Sie uns in sozialen Netzwerken