Политика конфиденциальностиКонтактыО сайтеОтзывыGitHubDonate
© 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 выбранной записи не пойму.
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 г. 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 эта модель работает отлично, а здесь в делегате по прежнему пусто. Может я не правильно понял или что-то не так делаю?
0
  • 23 июля 2018 г. 11: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 г. 12:01

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

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


0

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

0

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

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

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

0

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

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

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

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

есть column и row.

0

Ответы

Только авторизованные пользователи могут отвечать на форуме.
Пожалуйста, авторизуйтесь или зарегистрируйтесь
ДД
13 декабря 2018 г. 16:24
Дмитрий Дубовик

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

  • Результат:66баллов,
  • Очки рейтинга-1
13 декабря 2018 г. 16:04
Metelev

Qt - Тест 001. Сигналы и слоты

  • Результат:47баллов,
  • Очки рейтинга-6
YC
12 декабря 2018 г. 18:49
Yaroslav Chernetskyi

Qt - Тест 001. Сигналы и слоты

  • Результат:31баллов,
  • Очки рейтинга-10
Последние комментарии
V
15 декабря 2018 г. 2:06
Vlad15007

Спасибо большое!Очень помогли!
11 декабря 2018 г. 21:01
Евгений Легоцкой

Не знаю, какой-там конкретно эффект и если честно не хочется fl studio ради того, чтобы посмотреть устанавливать, но из того, что увидел в интернете. Предполагаю, что то, что вы хотите с...
V
11 декабря 2018 г. 19:25
Vlad15007

Подскажите пожалуйста ( я новичок совсем)Можно ли организовать спрайт без этого окошка (как в fl studio fruity dance)?
11 декабря 2018 г. 15:06
Евгений Легоцкой

Что интересно, если написать так from <application_name>.<module_name> import <filename> ,то PyCharm сносит крышу, если разрабатываешь в рамках проекта приложение, ко...
11 декабря 2018 г. 14:52
Илья Чичак

Тут мне тоже есть что сказать=) Сами разрабы советуют импортировать следующим образом: from <application_name> import <module_name> Стоит избегать from . import &l...;
Сейчас обсуждают на форуме
17 декабря 2018 г. 17:55
Евгений Легоцкой

Просчитывать перекрытие точек и не отрисовывать те точки, которые перекрываются другими. У вас их просто слишком много, нужно смотреть, какие можно не отрисовывать без потери информативн...
R
16 декабря 2018 г. 14:41
RED_Spider

перевірено все працює http://doc.qt.io/qt-5/appicon.html Setting the Application Icon on Windows First, create an ICO format bitmap file that contains the icon image. This ca...
16 декабря 2018 г. 11:26
Евгений Легоцкой

Только статические методы и участники класса можно вызывать подобным образом Cell::sum У вас же они нестатические, чтобы их вызывать, нужно иметь объект Cell. Вы его, конечно, со...
q
15 декабря 2018 г. 23:02
qdu10719

Понял, спасибо большое
БГ
14 декабря 2018 г. 17:44
Булат Гиниятов

Большое всем спасибо за помощь! Использую вариант с QList.
Присоединяйтесь к нам в социальных сетях

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