Страницы

Поиск по вопросам

понедельник, 24 февраля 2020 г.

Qt выпадающий список в tableview

#cpp #qt #sqlite #tableview


Как организовать выпадающий список в tableview? Использую databinding с sqlite. В
одном из столбцов есть элементы, которые нужно представить на выбор выпадающим списком.
    


Ответы

Ответ 1



Если данные выпадающего списка содержатся в отдельной таблице базы данных, то удобно использовать QSqlRelationalTableModel и класс делегата QSqlRelationalDelegate. Для демонстрации предположим, что имеется таблица городов: CREATE TABLE IF NOT EXISTS `cities` ( `city_id` INTEGER UNSIGNED NOT NULL , `name` VARCHAR(50) NOT NULL , PRIMARY KEY(`city_id`) ) ... и таблица сотрудников, которые работают в этих городах: CREATE TABLE IF NOT EXISTS `employees` ( `employee_id` INTEGER UNSIGNED NOT NULL , `city_id` INTEGER NOT NULL , `name` VARCHAR(255) NOT NULL , PRIMARY KEY(`employee_id`) ) Заполним эти таблицы данными: INSERT INTO `cities` VALUES(1, 'Москва') INSERT INTO `cities` VALUES(2, 'Санкт-Петербург') INSERT INTO `employees` VALUES(1, 1, 'Иван Иваныч') INSERT INTO `employees` VALUES(2, 2, 'Пётр Петрович') Иван Иваныч работает в Москве, а Пётр Петрович в Санкт-Петербурге. Теперь создадим модель и представление таблицы с использованием Qt: QSqlRelationalTableModel *model = new QSqlRelationalTableModel(); model->setTable("employees"); model->setEditStrategy(QSqlTableModel::OnManualSubmit); model->setRelation(1, QSqlRelation("cities", "city_id", "name")); model->setHeaderData(0, Qt::Horizontal, QObject::tr("ID")); model->setHeaderData(1, Qt::Horizontal, QObject::tr("City")); model->setHeaderData(2, Qt::Horizontal, QObject::tr("Name")); QTableView *view = new QTableView(); view->setModel(model); Ключевой строкой кода при объединении данных двух таблиц является использование метода setRelation() у QSqlRelationalTableModel. Первый аргумент метода - это индекс колонки в исходной таблице (в нашем случае employees), второй - объект объединения QSqlRelation, в конструкторе которого указываются наименование подключаемой таблицы, наименование её колонки, соответствующей исходной, плюс наименование колонки, содержимое которой должно подменять содержимое в ячейке исходной таблицы, если соответствие будет обнаружено. Если запустить программу, то в таблице сотрудников вместо идентификаторов городов окажутся их наименования. Однако при попытке редактирования ячейки города раскрывающийся список с доступными наименованиями не появится. Для решения этой стороны вопроса достаточно подключить объект делегата QSqlRelationalDelegate к представлению: view->setItemDelegateForColumn(1, new QSqlRelationalDelegate(view)); Разумеется, всё это будет работать только в том случае, если исходная модель получена наследованием от QSqlRelationalTableModel или прямым использованием объекта этого класса. Это не во всех случаях представляется возможным. Например, если модель получает данные из сети. Для этого случая предположим, что имеющиеся таблицы employees и cities содержатся не в БД, а лишь как данные, например, в произвольном наследнике QAbstractItemModel. Также как и в первом случае потребуется делегат, который будет выполнять роль связующего звена между таблицами сотрудников и городов. Файл comboboxitemdelegate.h: #ifndef COMBOBOXITEMDELEGATE_H #define COMBOBOXITEMDELEGATE_H #include #include class QAbstractItemModel; class ComboBoxItemDelegate : public QStyledItemDelegate { Q_OBJECT public: explicit ComboBoxItemDelegate(QObject *parent = Q_NULLPTR); virtual ~ComboBoxItemDelegate() {} QAbstractItemModel *model() const; void setModel(QAbstractItemModel *model); int modelKeyColumn() const; void setModelKeyColumn(int column); int modelViewColumn() const; void setModelViewColumn(int column); virtual QString displayText(const QVariant &value , const QLocale &locale) const; virtual QWidget *createEditor(QWidget *parent , const QStyleOptionViewItem &option , const QModelIndex &index) const; virtual void setModelData(QWidget *editor , QAbstractItemModel *model , const QModelIndex &index) const; private: int _model_key_column, _model_view_column; QPointer _model; }; #endif Файл comboboxitemdelegate.cpp: #include #include "comboboxitemdelegate.h" ComboBoxItemDelegate::ComboBoxItemDelegate(QObject *parent) : QStyledItemDelegate(parent) , _model_key_column(0), _model_view_column(0) {} QAbstractItemModel *ComboBoxItemDelegate::model() const { return _model; } void ComboBoxItemDelegate::setModel(QAbstractItemModel *model) { _model = model; } int ComboBoxItemDelegate::modelKeyColumn() const { return _model_key_column; } void ComboBoxItemDelegate::setModelKeyColumn(int column) { _model_key_column = column; } int ComboBoxItemDelegate::modelViewColumn() const { return _model_view_column; } void ComboBoxItemDelegate::setModelViewColumn(int column) { _model_view_column = column; } QString ComboBoxItemDelegate::displayText(const QVariant &value , const QLocale &locale) const { Q_UNUSED(locale); if(_model.isNull()) return QString(); while(_model->canFetchMore(QModelIndex())) _model->fetchMore(QModelIndex()); QModelIndexList indexes = _model->match(_model->index(0,_model_key_column) , Qt::DisplayRole, value, 1, Qt::MatchExactly); if(indexes.isEmpty()) return QString(); QModelIndex index = _model->index(indexes.first().row(), _model_view_column); return (index.isValid()) ? _model->data(index).toString() : QString(); } QWidget *ComboBoxItemDelegate::createEditor(QWidget *parent , const QStyleOptionViewItem &option, const QModelIndex &index) const { if(_model.isNull()) return; while(_model->canFetchMore(QModelIndex())) _model->fetchMore(QModelIndex()); QComboBox *cbox = new QComboBox(parent); cbox->setGeometry(option.rect); cbox->setModel(_model); cbox->setModelColumn(_model_view_column); QModelIndexList indexes = _model->match(_model->index(0,_model_key_column) , Qt::DisplayRole, index.data(), 1, Qt::MatchExactly); if(!indexes.isEmpty()) cbox->setCurrentIndex(indexes.first().row()); return cbox; } void ComboBoxItemDelegate::setModelData(QWidget *editor , QAbstractItemModel *model, const QModelIndex &index) const { if(_model.isNull()) return; QComboBox *cbox = qobject_cast(editor); if(cbox == Q_NULLPTR) return; while(_model->canFetchMore(QModelIndex())) _model->fetchMore(QModelIndex()); QModelIndex model_index = _model->index(cbox->currentIndex(), _model_key_column); if(model_index.isValid()) model->setData(index, model_index.data()); } Представленный делегает не претендует на применение и опубликован с целью демонстрации механизма ассоциации значений между моделями. Подключение и использование делегата: QAbstractItemModel *employees = ... ; QAbstractItemModel *cities = ... ; QTableView *view = new QTableView(); view->setModel(employees); ComboBoxItemDelegate *delegate = new ComboBoxItemDelegate(view); delegate->setModel(cities); delegate->setModelKeyColumn(0); delegate->setModelViewColumn(1); view->setItemDelegateForColumn(1, delegate);

Ответ 2



Вы хоть уточните про который tableview речь-то идет - QTableView из widgets или про TableView из QtQuick.Controls. В целом в обоих случаях пишется делегат, для первого случая у QTableView (точнее у его предка) есть метод setItemDelegateForColumn, которому скармливается номер колонки и потомка QAbstractItemDelegate. Такового потомка я когда-то для себя писал и насколько помню он даже работал, но это было давно и работает ли он сейчас не знаю. Со вторым случаем все проще - у TableViewColumn есть соотвествующее свойство delegate куда вставляется qml Item (например, как тут, хотя скорее всего можно было и без Item обойтись), но сам я на практике этим не пользовался насколько помню, да и вообще с комбобоксом из QtQuick.Controls у меня натянутые отношения - то он не редактируемый, то редактируемый но на пробел забито раскрытие списка (поправили только в 5.4.1), то программно не устанавливается пустая строка в качестве значения

Комментариев нет:

Отправить комментарий