概要
練習問題3に入る前にQTableViewの使い方を改めて確認する
[テーブルの項目]
1列目:チェックボックス
2列目:テキスト
3列目:ステータス
[QTableView:サンプル]
#1:チェックボックスを使用する(1列目)- 完了
#2:テキストを編集する(2列目)- 完了
#3:ドロップダウンリストを使用する(3列目)- 完了
[ソースコード]
https://github.com/dctk/qtqbleview_practice.git
テーブル用のクラス
テーブルのデータ:MyData(継承:QObject)
テーブルのモデル:MyDataTableModel(継承:QAbstractTableModel)
ウィンドウ :Widget(継承:QWidget)
サンプル1
テーブルの1列目をチェックボックスにする
重要な箇所
テーブルのモデルで以下のメソッドをオーバーライドする
// 特定の列に「選択可」「チェック可」フラグを設定する
MyDataTableModel::flags
// チェックボックスの状態を保存する
MyDataTableModel::setData
// ロールがCheckStateRoleの時にチェック状態をreturnする
// → Qt::Checked or Qt::UnChecked
MyDataTableModel::data
ウィンドウ
Header
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private slots:
void on_pushButton_clicked();
private:
Ui::Widget *ui;
MyDataTableModel *model;
};
Source
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
model = new MyDataTableModel();
ui->tableView->setModel(model);
// Windowsサイズ変更時にテーブル等を自動リサイズするための設定
setLayout(ui->verticalLayout);
}
Widget::~Widget()
{
delete model;
delete ui;
}
void Widget::on_pushButton_clicked()
{
// ダミーデータの追加
MyData *data = new MyData();
data->setText(QString("Hello"));
data->setCheck(false);
data->setSatus(MyData::Status::InProgras);
model->add(data);
}
データ
Header
class MyData : public QObject
{
Q_OBJECT
Q_PROPERTY(bool check READ check WRITE setCheck NOTIFY checkChanged)
Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
Q_PROPERTY(Status status READ status WRITE setSatus NOTIFY statusChanged)
public:
explicit MyData(QObject *parent = nullptr);
typedef enum {
NotStarted, // タスク未着手
InProgras, // タスク進行中
Finish // タスク完了
} Status;
// データのチェック状態(Viewのチェック状態) ←いらないかも・・・
bool check() const;
void setCheck(bool newCheck);
// テキスト
const QString &text() const;
void setText(const QString &newText);
// タスクの状態
Status status() const;
void setSatus(Status newStatus);
signals:
void checkChanged();
void textChanged();
void statusChanged();
private:
bool m_check;
QString m_text;
Status m_status;
};
Source
MyData::MyData(QObject *parent)
: QObject{parent}
{
}
bool MyData::check() const
{
return m_check;
}
void MyData::setCheck(bool newCheck)
{
if (m_check == newCheck)
return;
m_check = newCheck;
emit checkChanged();
}
const QString &MyData::text() const
{
return m_text;
}
void MyData::setText(const QString &newText)
{
if (m_text == newText)
return;
m_text = newText;
emit textChanged();
}
MyData::Status MyData::status() const
{
return m_status;
}
void MyData::setSatus(Status newStatus)
{
if (m_status == newStatus)
return;
m_status = newStatus;
emit statusChanged();
}
データモデル
header
class MyDataTableModel : public QAbstractTableModel
{
public:
explicit MyDataTableModel(QObject *parent = nullptr);
~MyDataTableModel();
// QAbstractItemModel interface
public:
int rowCount(const QModelIndex &parent) const;
int columnCount(const QModelIndex &parent) const;
QVariant data(const QModelIndex &index, int role) const;
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
public:
void add(MyData *data);
private:
QVector<MyData*> items;
// QAbstractItemModel interface
public:
bool setData(const QModelIndex &index, const QVariant &value, int role);
// QAbstractItemModel interface
public:
Qt::ItemFlags flags(const QModelIndex &index) const;
};
source
//https://doc.qt.io/qt-5/modelview.html
MyDataTableModel::MyDataTableModel(QObject *parent)
: QAbstractTableModel{parent}
{
}
MyDataTableModel::~MyDataTableModel()
{
}
int MyDataTableModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return items.count();
}
int MyDataTableModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return 3;
}
QVariant MyDataTableModel::data(const QModelIndex &index, int role) const
{
int row = index.row();
int column = index.column();
if (!index.isValid()){
return QVariant();
}
if (role == Qt::CheckStateRole && column == 0){
if(items[row]->check()){
return Qt::Checked;
}
else{
return Qt::Unchecked;
}
}
else if (role == Qt::DisplayRole){
switch(column){
case 0:
if (items[row]->check()){
return "選択中";
}
else{
return QVariant();
}
case 1:
return items[row]->text();
case 2:
return items[row]->status();
default:
Q_UNREACHABLE();
break;
}
}
return QVariant();
}
QVariant MyDataTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role != Qt::DisplayRole){
return QVariant();
}
if (orientation == Qt::Horizontal){
switch(section){
case 0:
return "選択";
case 1:
return "テキスト";
case 2:
return "状態";
default:
Q_UNREACHABLE();
break;
}
}
return QVariant();
}
void MyDataTableModel::add(MyData *data)
{
beginInsertRows(QModelIndex(),items.count(),items.count());
items.push_back(data);
endInsertRows();
}
bool MyDataTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (!index.isValid()){
return false;
}
int column = index.column();
int row = index.row();
if (role == Qt::CheckStateRole){
if (column == 0){
if (value == Qt::Checked){
items[row]->setCheck(true);
emit dataChanged(index,index);
}
else if (value == Qt::Unchecked){
items[row]->setCheck(false);
emit dataChanged(index,index);
}
}
}
return QAbstractItemModel::setData(index, value, role);
}
Qt::ItemFlags MyDataTableModel::flags(const QModelIndex &index) const
{
Qt::ItemFlags flag = QAbstractItemModel::flags(index);
int column = index.column();
if (column == 0){
flag |= Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
}
return flag;
}
サンプル2
テーブルの2列目を編集可能(テキスト)にする
対応方法:
データモデルを修正する
・flags
・data
・setData
Qt::ItemFlags MyDataTableModel::flags(const QModelIndex &index) const
{
・・・
// 2列目に有効+編集可能フラグを追加する
+else if (column == 1){
+ flag |= Qt::ItemIsEnabled | Qt::ItemIsEditable;
+}
・・・
}
QVariant MyDataTableModel::data(const QModelIndex &index, int role) const
{
・・・
if (role == Qt::CheckStateRole && column == 0){
・・・
}
// 変更前の値を返す
+ else if (role == Qt::EditRole){
+ if (column == 1){
+ QVariant value = items[row]->text();
+ return value;
+ }
}
else if (role == Qt::DisplayRole){
・・・
}
・・・
}
bool MyDataTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
・・・
if (role == Qt::CheckStateRole){
・・・
}
// 編集後の値をモデルアイテムに設定する
else if (role == Qt::EditRole){
if (column == 1){
items[row]->setText(value.toString());
emit dataChanged(index,index);
}
}
・・・
}
サンプル3
テーブルの3列目を編集可能(コンボボックス)にする
対応方法:
1. ItemDelegeteを継承したクラスを作成する
2. QTableViewModelを編集可能にする
・flags
・data
・setData
3. QTableViewModelに作成したデリゲートを設定する
ItemDelegateを継承したクラスを作成する
#ifndef COMBOBOXITEMDELEGATE_H
#define COMBOBOXITEMDELEGATE_H
#include <QItemDelegate>
class QComboBoxItemDelegate : public QItemDelegate
{
Q_OBJECT
public:
explicit QComboBoxItemDelegate(QObject *parent = nullptr);
// QAbstractItemDelegate interface
public:
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 add(QString data);
private:
QStringList items;
};
#endif // COMBOBOXITEMDELEGATE_H
#include "comboboxitemdelegate.h"
#include <QComboBox>
QComboBoxItemDelegate::QComboBoxItemDelegate(QObject *parent)
: QItemDelegate{parent}
{
}
// 編集モードになった時に呼び出される
QWidget *QComboBoxItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QComboBox *combobox = new QComboBox(parent);
combobox->addItems(items);
if (items.length() > 0){
combobox->setCurrentIndex(0);
}
return combobox;
}
// 編集モードになった後、コンボボックスのデータをセットする際に呼び出される
void QComboBoxItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
QComboBox *combobox = static_cast<QComboBox*>(editor);
int value = index.model()->data(index,Qt::EditRole).toUInt();
return combobox->setCurrentIndex(value);
}
// 編集内容が確定?した時に呼び出される
void QComboBoxItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
QComboBox *combobox = static_cast<QComboBox*>(editor);
model->setData(index,combobox->currentIndex(),Qt::EditRole);
}
// 自分自身で呼び出す(コンボボックスのアイテムを追加する)
void QComboBoxItemDelegate::add(QString data)
{
items.append(data);
}
QTableViewModelを編集可能にする
Qt::ItemFlags MyDataTableModel::flags(const QModelIndex &index) const
{
・・・
if (column == 0){
・・・
}
else if (column == 1){
・・・
}
else if (column == 2){
flag |= Qt::ItemIsEnabled | Qt::ItemIsEditable;
}
・・・
}
bool MyDataTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
・・・
if (role == Qt::CheckStateRole){
・・・
}
else if (role == Qt::EditRole){
if (column == 1){
・・・
}
else if (column == 2){
switch(value.toUInt()){
case 0:
items[row]->setSatus(MyData::Status::NotStarted);
break;
case 1:
items[row]->setSatus(MyData::Status::InProgras);
break;
case 2:
items[row]->setSatus(MyData::Status::Finish);
break;
}
}
}
・・・
}
QVariant MyDataTableModel::data(const QModelIndex &index, int role) const
{
・・・
if (role == Qt::CheckStateRole && column == 0){
・・・
}
else if (role == Qt::EditRole){
if (column == 1){
・・・
}
else if (column == 2){
QVariant value = items[row]->status();
return value;
}
}
else if (role == Qt::DisplayRole){
switch(column){
・・・
case 2:
switch(items[row]->status()){
case MyData::Status::NotStarted:
return "未着手";
break;
case MyData::Status::InProgras:
return "進行中";
break;
case MyData::Status::Finish:
return "完了";
break;
}
return items[row]->status();
・・・
}
}
・・・
}
QTableViewModelに作成したデリゲートを設定する
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
・・・
QComboBoxItemDelegate *d = new QComboBoxItemDelegate(this);
d->add("未着手");
d->add("進行中");
d->add("完了");
ui->tableView->setItemDelegateForColumn(2,d);
・・・
}