QTableWidget(...view)自定义委托(combobox,spinbox,checkbox,dateTimeEdit)

简述

       在经典的 MVC 模型中,view用于向用户展示 model 的数据。但是,Qt提供的不是 MVC 三层架构,而是一个 model/view 设计。这种设计并没有包含一个完整而独立的组件用于管理用户的交互。在这种结构中,为了获得对用户输入控制的灵活性,这种交互工作交给了delegate(委托)去完成。简单来说,就像它们的名字一样,view 将用户输入委托给 delegate 处理,而自己不去处理这种输入。这些组件提供一种输入能力,并且能够在某些 view 中提供这种交互情形下的渲染,比如在 table 中通过双击单元格即可编辑内容等。对这种控制委托的标准接口被定义在 QAbstractItemDelegate 类中。

        Qt提供的标准组件使用 QItemDelegate 提供编辑功能的支持。这种默认的实现被用在 QListView,QTableView 和 QTreeView 之中。view 实用的delegate可以通过 itemDelegate() 函数获得。setItemDelegate() 函数则可以为一个标准组件设置自定义的 delegate。delegate 可以用于渲染内容,这是通过 paint() 和 sizeHint() 函数来完成的。这个例子中,我们继承了QItemDelegate,重写了里面的createEditor(),setModelData()和paint()三个函数。给view 组件提供绘制和编辑的功能,来实现在TableView中添加Combobox、Spainbox、CheckBox控件。

效果

代码部分

        一个自定义的delegate可以直接提供一个编辑器,而不是使用内置的编辑器工厂(editor item factory)。如果你需要这种功能,那么需要重新实现一下几个函数:

  • createEditor(): 返回修改数据的组件;
  • setEditorData(): 为editor提供编辑的原始数据;
  • updateEditorGeometry(): 保证editor显示在 item view 的合适位置以及大小;
  • setModelData(): 根据editor 的数据更新model的数据。

下面来看代码:

controldelegate.h文件

#define CONTROLDELEGATE_H
 
#include <QObject>
#include <QSpinBox>
#include <QCheckBox>
#include <QComboBox>
#include <QItemDelegate>
#include <QApplication>
#include <QStyledItemDelegate>
#include <QStyleOption>
#include <QMouseEvent>
#include <QPainter>
#include <QDateTimeEdit>
 
class SpinboxDelegate : public QItemDelegate
{
        Q_OBJECT
 
public:
    SpinboxDelegate(int column);
    //重写QItemDelegate里的函数
    //const修饰引用传递,"引用传递"仅借用一下参数的别名,不需要产生临时对象,所以可以提高效率.使用const可以确保引用的参数不被修改
    //const加在最后,函数的数据成员mColumn不可被修改
    virtual QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const;
    //void setEditorData(QWidget *editor, const QModelIndex &index) const ;       //为editor提供编辑的原始数据
    virtual void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const ;
    virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
    //void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const;     //保证editor显示在 item view 的合适位置以及大小
 
private slots:
    void commitAndCloseEditor();
 
private:
    int mColumn;
};
 
class CheckBoxDelegate : public QStyledItemDelegate
{
    Q_OBJECT
 
public:
    CheckBoxDelegate(QObject *parent = 0);
protected:
    void paint(QPainter* painter,const QStyleOptionViewItem& option,const QModelIndex& index) const;
    bool editorEvent(QEvent *event,QAbstractItemModel *model,const QStyleOptionViewItem &option,const QModelIndex &index);
};
 
class ComboboxDelegate : public QItemDelegate
{
    Q_OBJECT
 
public:
    ComboboxDelegate();
    QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const;
    void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
 
private slots:
    void commitAndCloseEditor();
};

class DateDelegate : public QItemDelegate
{
    Q_OBJECT

public:
    DateDelegate(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 // CONTROLDELEGATE_H

controldelegate.cpp文件

#include "controldelegate.h"
 
//spinbox控件部分
SpinboxDelegate::SpinboxDelegate(int column)
{
        mColumn = column;
}
 
//返回修改数据的组件,为指定的列或者行创建部件
QWidget *SpinboxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option,const QModelIndex &index) const
{
    if(index.column() == 3)
    {
        QSpinBox *editor = new QSpinBox(parent);
        editor->setRange(0 , 1000);
        connect(editor,SIGNAL(editingFinished()),SLOT(commitAndCloseEditor()));
        return editor;
    }
    else if( index.column() == 4)
    {
        QSpinBox *editor = new QSpinBox(parent);
        editor->setRange(0,1000);
        connect(editor,SIGNAL(editingFinished()),SLOT(commitAndCloseEditor()));
        return editor;
    }
    else
    {
        return QItemDelegate::createEditor(parent,option,index);
    }
}
 
//根据editor 的数据更新model的数据
void SpinboxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
    QSpinBox *spinBox = static_cast<QSpinBox*>(editor);
    spinBox->interpretText();
    int value = spinBox->value();
 
    model->setData(index, value, Qt::EditRole);
}
 
//显示格式控制
void SpinboxDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    if(index.column() == 3) {
        int warehouseAmount = index.model()->data(index,Qt::DisplayRole).toInt();
        QString text = QString("%1").arg(warehouseAmount, 3, 10, QChar(' '));
 
        QStyleOptionViewItem myOption = option;
        //设置显示在item的中间
        myOption.displayAlignment = Qt::AlignHCenter | Qt::AlignVCenter;
 
        drawDisplay(painter, myOption, myOption.rect, text);
        drawFocus(painter, myOption, myOption.rect);
    }
    else if(index.column() == 4) {
        int amount = index.model()->data(index,Qt::DisplayRole).toInt();
        QString text = QString("%1").arg(amount, 3, 10, QChar(' '));
 
        QStyleOptionViewItem myOption = option;
        myOption.displayAlignment = Qt::AlignHCenter | Qt::AlignVCenter;
 
        drawDisplay(painter, myOption, myOption.rect, text);
        drawFocus(painter, myOption, myOption.rect);
    }
}
 
void SpinboxDelegate::commitAndCloseEditor()
{
    QSpinBox *editor = qobject_cast<QSpinBox*>(sender());
    emit commitData(editor);                    //当编辑器小部件完成数据编辑并希望将其写入模型时,必须发出此信号
    emit closeEditor(editor);                   //当用户使用指定的编辑器完成对项目的编辑时,将发出此信号
}
 
//checkbox控件部分
static QRect CheckBoxRect(const QStyleOptionViewItem &viewItemStyleOptions)/*const*/
{
    //绘制按钮所需要的参数
    QStyleOptionButton checkBoxStyleOption;
    //按照给定的风格参数 返回元素子区域
    QRect checkBoxRect = QApplication::style()->subElementRect( QStyle::SE_CheckBoxIndicator, &checkBoxStyleOption);
    //返回QCheckBox坐标
    QPoint checkBoxPoint(viewItemStyleOptions.rect.x() + viewItemStyleOptions.rect.width() / 2 - checkBoxRect.width() / 2,
                         viewItemStyleOptions.rect.y() + viewItemStyleOptions.rect.height() / 2 - checkBoxRect.height() / 2);
    //返回QCheckBox几何形状
    return QRect(checkBoxPoint, checkBoxRect.size());
}
 
CheckBoxDelegate::CheckBoxDelegate(QObject *parent):
    QStyledItemDelegate(parent)
{
 
}
 
void CheckBoxDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option,const QModelIndex& index)const
{
    bool checked = index.model()->data(index, Qt::DisplayRole).toBool();
 
    if(index.column() == 5) {
        //按钮的风格选项
        QStyleOptionButton checkBoxStyleOption;
        //|=为复合赋值语句,a|=b等价于a=a|b,对a和b进行位或运算
        checkBoxStyleOption.state |= QStyle::State_Enabled;
 
        //根据值判断是否选中
        checkBoxStyleOption.state |= checked? QStyle::State_On : QStyle::State_Off;
 
        //返回QCheckBox几何形状
        checkBoxStyleOption.rect = CheckBoxRect(option);
        //绘制QCheckBox
        QApplication::style()->drawControl(QStyle::CE_CheckBox, &checkBoxStyleOption, painter);
    }
    else {
        //否则调用默认委托
        QStyledItemDelegate::paint(painter, option, index);
    }
}
 
bool CheckBoxDelegate::editorEvent(QEvent *event,
                                QAbstractItemModel *model,
                                const QStyleOptionViewItem &option,
                                const QModelIndex &index) {
    if(index.column() == 5){
    if((event->type() == QEvent::MouseButtonRelease) ||
            (event->type() == QEvent::MouseButtonDblClick)){
        QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
        if(mouseEvent->button() != Qt::LeftButton ||
                !CheckBoxRect(option).contains(mouseEvent->pos())){
            return true;
        }
        if(event->type() == QEvent::MouseButtonDblClick){
            return true;
        }
    }else if(event->type() == QEvent::KeyPress){
        if(static_cast<QKeyEvent*>(event)->key() != Qt::Key_Space &&
                static_cast<QKeyEvent*>(event)->key() != Qt::Key_Select){
            return false;
        }
    }else{
        return false;
    }
 
    bool checked = index.model()->data(index, Qt::DisplayRole).toBool();
    return model->setData(index, !checked, Qt::EditRole);
    }else{
        return QStyledItemDelegate::editorEvent(event, model, option, index);
    }
}
 
//combobox控件部分
ComboboxDelegate::ComboboxDelegate()
{
 
}
 
QWidget *ComboboxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    QComboBox *editor = new QComboBox(parent);
    if(index.column() == 2){
        editor->addItem(QString::fromLocal8Bit("boy"));
        editor->addItem(QString::fromLocal8Bit("girl"));
        editor->setCurrentIndex(0);
 
        connect(editor,SIGNAL(editingFinished()),SLOT(commitAndCloseEditor()));
        return editor;
    }
    else
    {
        QItemDelegate::createEditor(parent,option,index);
    }
}
 
void ComboboxDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    if(index.column() == 2)
    {
        QString text = index.model()->data(index , Qt::DisplayRole).toString();
 
        QStyleOptionViewItem myOption = option;
        myOption.displayAlignment = Qt::AlignHCenter | Qt::AlignVCenter;
 
        drawDisplay(painter,myOption,myOption.rect,text);
        drawFocus(painter,myOption,myOption.rect);
    }
    else
    {
        QItemDelegate::paint(painter,option,index);
    }
}
 
void ComboboxDelegate::commitAndCloseEditor()
{
    QComboBox *editor = qobject_cast<QComboBox*>(sender());
    emit commitData(editor);
    emit closeEditor(editor);
}

//Date控件部分
//Date控件部分
DateDelegate::DateDelegate(QObject *parent)
    : QItemDelegate(parent)
{
}

QWidget *DateDelegate::createEditor(QWidget *parent,
    const QStyleOptionViewItem &/* option */,
    const QModelIndex &/* index */) const
{
    QDateTimeEdit *editor = new QDateTimeEdit(parent);
    editor->setDisplayFormat("yyyy-MM-dd");
    editor->setCalendarPopup(true);
    editor->installEventFilter(const_cast<DateDelegate*>(this));

    return editor;
}

void DateDelegate::setEditorData(QWidget *editor,
                                     const QModelIndex &index) const
{
    QString dateStr = index.model()->data(index).toString();
    QDate date = QDate::fromString(dateStr,Qt::ISODate);

    QDateTimeEdit *edit = static_cast<QDateTimeEdit*>(editor);
    edit->setDate(date);
}

void DateDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
                                    const QModelIndex &index) const
{
    QDateTimeEdit *edit = static_cast<QDateTimeEdit*>(editor);
    QDate date = edit->date();

    model->setData(index, QVariant(date.toString(Qt::ISODate)));
}

void DateDelegate::updateEditorGeometry(QWidget *editor,
    const QStyleOptionViewItem &option, const QModelIndex &/* index */) const
{
    editor->setGeometry(option.rect);
}

mainwindow.h文件

#ifndef MAINWINDOW_H
#define MAINWINDOW_H
 
#include <QMainWindow>
#include <QStandardItemModel>
#include "controldelegate.h"
 
namespace Ui {
class MainWindow;
}
 
class MainWindow : public QMainWindow
{
    Q_OBJECT
 
public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();
 
    void tableViewInit();
 
private slots:
    void contextMenuEvent(QContextMenuEvent *event);
 
    //插入行操作
    void sltInsertAction();
 
    //删除指定行操作
    void sltDeleteAction();
 
private:
    Ui::MainWindow *ui;
 
    QMenu *OperationMenu;                   //操作菜单
    QAction *DeleteAction;                  //删除动作
    QAction *InsertAction;                  //插入动作
 
    QStandardItemModel *tableModel;
};
 
#endif // MAINWINDOW_H

mainwindow.cpp文件

#include "ui_mainwindow.h"
 
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
 
    tableViewInit();
 
    OperationMenu = new QMenu();
    DeleteAction = new QAction("删除");
    InsertAction = new QAction("插入");
    OperationMenu->addAction(DeleteAction);
    OperationMenu->addAction(InsertAction);
 
    connect(InsertAction, SIGNAL(triggered(bool)), this, SLOT(sltInsertAction()));
    connect(DeleteAction, SIGNAL(triggered(bool)), this, SLOT(sltDeleteAction()));
}
 
MainWindow::~MainWindow()
{
    delete ui;
}
 
void MainWindow::contextMenuEvent(QContextMenuEvent *event) //重写上下文事件
{
    //方法一
    if(ui->tableView->hasFocus())
    {
        OperationMenu->move(cursor().pos());
        OperationMenu->show();
    }
    //方法二
//    {
//        ui->tableView->addAction(InsertAction);
//        ui->tableView->addAction(DeleteAction);
//        ui->tableView->(Qt::ActionsContextMenu);
//    }
}
 
void MainWindow::tableViewInit()
{
    tableModel = new QStandardItemModel;
    ui->tableView->setModel(tableModel);
 
    tableModel->setHorizontalHeaderItem(0, new QStandardItem("姓名"));
    tableModel->setHorizontalHeaderItem(1, new QStandardItem("日期"));
    tableModel->setHorizontalHeaderItem(2, new QStandardItem("性别"));
    tableModel->setHorizontalHeaderItem(3, new QStandardItem("语文"));
    tableModel->setHorizontalHeaderItem(4, new QStandardItem("数学"));
    tableModel->setHorizontalHeaderItem(5, new QStandardItem("测试通过"));
 
    ui->tableView->setModel(tableModel);
 
    //tableView委托设置每一列里的控件
    ui->tableView->setItemDelegateForColumn(1, new DateDelegate());
    ui->tableView->setItemDelegateForColumn(2, new ComboboxDelegate());
    ui->tableView->setItemDelegateForColumn(3, new SpinboxDelegate(3));
    ui->tableView->setItemDelegateForColumn(4, new SpinboxDelegate(4));
    ui->tableView->setItemDelegateForColumn(5, new CheckBoxDelegate());
 
    ui->tableView->setEditTriggers(QAbstractItemView::DoubleClicked);
    ui->tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
}
 
//插入行操作
void MainWindow::sltInsertAction()
{
    QList<QStandardItem*> item;
    item.append(new QStandardItem());
    item.append(new QStandardItem());
    item.append(new QStandardItem());
    item.append(new QStandardItem());
    item.append(new QStandardItem());
    item.append(new QStandardItem());
    //设置列数的对齐方式
    item.at(0)->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
    item.at(1)->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
    item.at(2)->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
    item.at(3)->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
    item.at(4)->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
    tableModel->insertRow(tableModel->rowCount(), item);
}
 
//删除指定行操作
void MainWindow::sltDeleteAction()
{
    int curreantRow = ui->tableView->currentIndex().row();                    //获取当前行数
    tableModel->removeRow(curreantRow);
}

文中实例文件下载 : QTableView自定义委托下载 提取码: ecp4

写在一个cpp文件里 : QTableView委托写在一个cpp文件里 提取码: 4gkc

转载自 : https://blog.csdn.net/Sakuya__/article/details/88895498

©️2020 CSDN 皮肤主题: 书香水墨 设计师:CSDN官方博客 返回首页