23 июля 2018 г. 5: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 выбранной записи не пойму.
Возврат 10% от суммы заказа отеля на Booking
Возврат 10% от суммы заказа отеля на Booking
Предлагаем ссылку с 10% возвратом от суммы заказа при бронировании отеля через Booking
14

У 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 или ещё что-то в этом роде.


0
  • 23 июля 2018 г. 6:19
  • (ред.)
Есть модель написанная под 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 эта модель работает отлично, а здесь в делегате по прежнему пусто. Может я не правильно понял или что-то не так делаю?
0
  • 23 июля 2018 г. 7:24

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

0

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

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, чтобы посмотреть, работает ли там в принципе что-нибудь.


0

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

Поэтому нужно переопределить метод setData и самостоятельно написать сырые SQL запросы для изменения данных.
0
Огромное спасибо за помощь!
Решил уйти от 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);

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

0
  • 24 июля 2018 г. 8:01

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

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


0

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

0

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

0
  • 24 июля 2018 г. 9:20

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

0

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

0
Да я уже понял. Спасибо!

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

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

есть column и row.

0

Комментарии

Только авторизованные пользователи могут публиковать комментарии.
Пожалуйста, авторизуйтесь или зарегистрируйтесь
МБ
14 июля 2019 г. 17:57
Максим Беликов

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

  • Результат:100баллов,
  • Очки рейтинга10
МБ
14 июля 2019 г. 17:52
Максим Беликов

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:90баллов,
  • Очки рейтинга8
МБ
14 июля 2019 г. 17:45
Максим Беликов

C++ - Тест 003. Условия и циклы

  • Результат:71баллов,
  • Очки рейтинга1
Последние комментарии
16 июля 2019 г. 15:15
Евгений Легоцкой

Конечно )) На самом деле не все читали Шлее... я например не читал ))
16 июля 2019 г. 15:05
IscanderChe

Так пример с QLocalServer и QLocalSocket я целиком переписал с QTcpSocket и QTcpServer из книги Шлее. Принципиальное отличие - где коннект ставить. :)) Разве что на это упор сделать. Но я могу...
16 июля 2019 г. 14:47
Евгений Легоцкой

Лучше API ))) Кстати, по использованию QLocalServer и QLocalSocket на сайте нет статей, было бы очень полезно и хорошо пошло бы в раздел Qt.
16 июля 2019 г. 12:36
IscanderChe

Да, прямой доступ не предполагается. Впрочем, можно, конечно, и напрямую. Всё равно всё локально происходит. Проблема в том, как донести сведения об изменениях в базе, которые вносит клиент, д...
b
16 июля 2019 г. 8:38
bbb116

спасибо, до smart pointer еще не дошел )
Сейчас обсуждают на форуме
17 июля 2019 г. 11:49
Михаиллл

В настройкак указан индификатор проекта и ключ. Осталось понять как использовать ключ. Попробовал вставить в этот запрос 'https://[PROJECT_ID].firebaseio/users/jack/name.json?access_to...
b
17 июля 2019 г. 9:01
bbb116

Вообщем работает только если делать setSceneRect только в конструкторе главного окна, если потом менять rect например при вставке картинки то появляются сколлы все как надо только области не р...
17 июля 2019 г. 5:54
Алексей Внуков

хочу не стандартный набор символов, а все ненужное убрать чтоб не мешало. для начала решил посмотреть как работает клава на родном примере, а он на телефоне не взлетел вот и начал разби...
17 июля 2019 г. 5:06
Михаиллл

Добрый день.Возможно вы сталкивались с облаками.Нужно из Qt создавать на облаке папки, записывать в папки файлы, читать и удалять с компьютера файлы.Возможно знаете, что лучше и проще испол...
Ищу работу?
25,000.00 руб. - 30,000.00 руб.
Разработчик Qt/C++
Barnaul, Altai Krai, Russia

Для зарегистрированных пользователей на сайте присутствует минимальное количество рекламы

EVILEG
О нас
Услуги
Присоединяйтесь к нам
© EVILEG 2015-2019
Рекомендует хостинг TIMEWEB