ゲスト 10人 と メンバー0人 がオンラインです

概要


練習問題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);

    ・・・
}