© EVILEG 2015-2018
Рекомендует хостинг
TIMEWEB
23 июля 2018 г. 9: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 выбранной записи не пойму.

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


Для Django рекомендую VDS-хостинг TIMEWEB

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

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

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

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


Для Django рекомендую VDS-хостинг TIMEWEB

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

Поэтому нужно переопределить метод setData и самостоятельно написать сырые SQL запросы для изменения данных.

Для Django рекомендую VDS-хостинг TIMEWEB

Огромное спасибо за помощь!
Решил уйти от 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);

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

  • #
  • 24 июля 2018 г. 12:01

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

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


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

Для Django рекомендую VDS-хостинг TIMEWEB

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

  • #
  • 24 июля 2018 г. 13:20

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

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

Для Django рекомендую VDS-хостинг TIMEWEB

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

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

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

есть column и row.

Ответы

Только авторизованные пользователи могут отвечать на форуме.
Пожалуйста, Авторизуйтесь или Зарегистрируйтесь
15 октября 2018 г. 21:36
Allyans .

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

  • Результат 60баллов,
  • Очки рейтинга-1
15 октября 2018 г. 11:25
Екатерина Самойлова

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

  • Результат 33баллов,
  • Очки рейтинга-10
15 октября 2018 г. 11:17
Екатерина Самойлова

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

  • Результат 80баллов,
  • Очки рейтинга4
Последние комментарии
10 октября 2018 г. 9:50
Евгений Легоцкой

Qt/C++ - Урок 083. Создание динамической библиотеки и подключение её в другой проект

Если и начинать писать о плагинах, то нужно тогда с Qt Creator начинать, там наверняка будет одинаковый принцип, но по Qt Creator хотя бы информация есть.
10 октября 2018 г. 9:48
ost.vld

Qt/C++ - Урок 083. Создание динамической библиотеки и подключение её в другой проект

О плагинах к QtCreator в целом, тоже интересно.
10 октября 2018 г. 9:46
ost.vld

Qt/C++ - Урок 083. Создание динамической библиотеки и подключение её в другой проект

ну типа того, создание программы, функционал которой можно расширять плагинами, и, в перспективе, создание API.
10 октября 2018 г. 8:31
Евгений Легоцкой

Qt/C++ - Урок 083. Создание динамической библиотеки и подключение её в другой проект

Типо как в Qt Creator? Самому бы интересно было о таком почитать. В данный момент я бы мог написать только о написании плагинов для Qt Designer. С этим есть некоторый опыт.
Сейчас обсуждают на форуме
15 октября 2018 г. 12:45
Allyans .

QGraphicsItem change color

Хорошо)
11 октября 2018 г. 10:13
Arrow

Работа с WebView в QML

Нашел в чем проблема. Пишу на случай если кому-то попадется такое же счастье с WebView как и мне. Проблема как оказалась с Debug версией, так как в Release и Profile все работает (...
10 октября 2018 г. 12:49
Виталий Антипов

Кто что делает на Qt?

Работаем по локальной сети. Файл базы, схемы и фото лежат на сервере. Чтобы не было проблем при одновременной работе с одним файлом, все запросы обернул в транзакции, как указано в документаци...
10 октября 2018 г. 11:21
Arrow

Редактирование записи на форуме

Добрый день! К сожалению встретил небольшой баг на форуме. При создании записи на форуме и вставке кода, через соответствующую кнопку номера строк проставляются верно, но каждый...
10 октября 2018 г. 9:46
Arrow

Настройка Qt Creator для Android

Я Genymotion  ставил с VBox в комплекте для личного использования, после добавил из списка телефон с нужным API. Запустил его и при компиляции Qt Creator сам нашел его и все запустилось...
Присоединяйтесь к нам в социальных сетях