Qt编程基础 | 第六章-窗体 | 6.3、QTableWidget

QTableWidget是QT中的表格组件,用于展示多行多列数据。文章介绍了如何设置表格的行数和列数,添加、删除单元格,以及表头的设置。此外,还讲解了如何设置选择行为、单元格控件,以及使用代理自定义显示和编辑方式。QTableWidgetItem、Item、Delegate和Model之间的数据交互关系也被详细阐述。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、QTableWidget

1、简介

QTableWidget是QT中的表格组件类,一般用来展示多行多列的数据,是QT中使用较多的控件之一。QTableWidgetItem用来表示表格中的一个单元格,整个表格都需要用逐个单元格构建起来。

2、QTableWidget常用方法

2.1、设置表格的行数与列数

// 设置表格列数
void QTableWidget::setColumnCount(int columns)
// 设置表格行数
void QTableWidget::setRowCount(int rows)
// 获取表格列数
int QTableWidget::columnCount() const
// 获取表格行数
int QTableWidget::rowCount() const

2.2、添加单元格

// 添加单元格
void QTableWidget::setItem(int row, int column, QTableWidgetItem *item)
// 合并单元格
void QTableView::setSpan(int row, int column, int rowSpanCount, int columnSpanCount)
// 清除单元格(包含表头)
void QTableWidget::clear()
// 清除单元格内容(不包含表头)
void QTableWidget::clearContents()

2.3、表头设置

/**************************** 水平表头设置 *****************************/
// 设置表头单元格
void QTableWidget::setHorizontalHeaderItem(int column, QTableWidgetItem *item)
// 设置表头显示的文本信息
void QTableWidget::setHorizontalHeaderLabels(const QStringList &labels)
// 获取表头单元格
QTableWidgetItem* QTableWidget::horizontalHeaderItem(int column) const

/**************************** 垂直表头设置 *****************************/
// 设置表头单元格
void QTableWidget::setVerticalHeaderItem(int row, QTableWidgetItem *item)
// 设置表头显示的文本信息
void QTableWidget::setVerticalHeaderLabels(const QStringList &labels)
// 获取表头单元格
QTableWidgetItem* QTableWidget::verticalHeaderItem(int row) const

2.4、设置行表头、列表头是否显示

/**************************** 行表头视图 *****************************/
// 获取行表头视图
QHeaderView *QTableView::horizontalHeader()

// 隐藏行表头
virtual void QWidget::setVisible(bool visible)

/**************************** 列表头视图 *****************************/
// 获取列表头视图
QHeaderView *QTableView::verticalHeader() 

// 隐藏列表头
virtual void QWidget::setVisible(bool visible)

2.5、设置选择行为

void QAbstractItemView::setSelectionBehavior(QAbstractItemView::SelectionBehavior behavior)
enum QAbstractItemView::SelectionBehavior功能
QAbstractItemView::SelectItems选中单个单元格
QAbstractItemView::SelectRows行选中
QAbstractItemView::SelectColumns列选中

2.6、设置单元格控件

QTableWidget不仅允许把文字加到单元格,还允许把控件也放到单元格中

// 获取单元格控件
QWidget *QTableWidget::cellWidget(int row, int column)

// 设置单元格控件
void QTableWidget::setCellWidget(int row, int column, QWidget *widget)

2.7、信号

下面是QTableWidget常用的信号,如下:

// 当item对应的data发生变化时,触发下面的信号
void QTableWidget::cellChanged(int row, int column)
// 双击单元格触发下面的信号
void QTableWidget::cellDoubleClicked(int row, int column)

3、QTableWidget代理

3.1、设置代理

可以给QTableWidget设置代理,通过代理能够自定义数据条目(item)的显示和编辑方式,QTableWidget提供的设置代理相关的方法如下:

// 给表格设置代理
void QAbstractItemView::setItemDelegate(QAbstractItemDelegate *delegate)
// 给列设置代理
void QAbstractItemView::setItemDelegateForColumn(int column, QAbstractItemDelegate *delegate)
// 给行设置代理
void QAbstractItemView::setItemDelegateForRow(int row, QAbstractItemDelegate *delegate)

注意 :只能给行、列、表格设置代理,不能给单个单元格设置代理

下面是Demo的截图,具体源码可以参考3.3

在这里插入图片描述

3.2、设置模型数据

QTableWidget默认使用的模型是QStandardItemModel,QTableWidget的每个单元格是一个QTableWidgetItem对象、Item、Delegate、Model三者间的数据交互关系如下:

在这里插入图片描述

// QModelIndex提供的获取模型的方法
const QAbstractItemModel *QModelIndex::model() const

// QTableWidgetItem提供的操作数据的方法
QVariant QTableWidgetItem::data(int role) const
void QTableWidgetItem::setData(int role, const QVariant &value)

// QAbstractItemModel提供的操作数据的方法
QVariant QAbstractItemModel::data(const QModelIndex &index, int role = Qt::DisplayRole) const
bool QAbstractItemModel::setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole)

注意:调用QTableWidgetItem::setData设置的数据,最终是设置到Model里面了,这一点Demo里面的onCliecked槽函数有验证

3.3、代理触发时机

3.4、示例

// qcustomwidget.h
#ifndef QCUSTOMWIDGET_H
#define QCUSTOMWIDGET_H

#include <QWidget>
#include <QTableWidget>
#include <QStyledItemDelegate>
#include <QObject>
#include <QPushButton>

struct RowData
{
    QString name;
    QString age;
    QString sex;
    QString number;
};

class LineEditDelegate : public QStyledItemDelegate
{
    Q_OBJECT

public:
    explicit LineEditDelegate(QObject* parent = nullptr);
    ~LineEditDelegate();

    QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
    void setEditorData(QWidget* editor, const QModelIndex& index) const override;
    void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const override;

private:
    QTableWidget *m_pTableWidget;
};

class ComboBoxDelegate : public QStyledItemDelegate
{
    Q_OBJECT

public:
    explicit ComboBoxDelegate(QObject* parent = nullptr);
    ~ComboBoxDelegate();

    QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
    void setEditorData(QWidget* editor, const QModelIndex& index) const override;
    void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const override;

private:
    QTableWidget *m_pTableWidget;
};


// 之所以要继承QTableWidget,是因为QTableWidget::indexFromItem方法是Protected,外面无法访问,继承QTableWidget,可以重新提供一个接口GetIndexFromItem
// 根据item获取对应的QModelIndex

// 主要是为了验证一个问题点:调用item->setData,会影响QTableWidget对应的model,即:item->setData设置的数据,会直接设置到model里面
class QMyTableWidget : public QTableWidget
{
    Q_OBJECT

public:
    QMyTableWidget(QWidget *parent = 0);

    QMyTableWidget(int rows, int columns, QWidget *parent = Q_NULLPTR);

    QModelIndex GetIndexFromItem(QTableWidgetItem *item) const;
};

class QCustomWidget : public QWidget
{
    Q_OBJECT

public:
    QCustomWidget(QWidget *parent = 0);
    ~QCustomWidget();

public:
    void Init();

private:
    // 创建控件
    void CreateWidgets();

    // 给控件添加布局
    void CreateLayout();

    // 初始化表格样式
    void InitTableStyle();

    // 初始化表格内容
    void InitTableContent();

    // 初始化表格显示的数据
    void InitTableData();

    // 初始化表格代理
    void InitTableDelegate();

public slots:
    void OnCliecked();
    void OnCellChanged(int row, int column);
    void OnCellDoubleClicked(int row, int column);


private:
    QMyTableWidget *m_pTableWidget;
    QPushButton *m_pPushButton;
    std::vector<RowData> m_rowData;
};

#endif // QCUSTOMWIDGET_H

// qcustomwidget.cpp
#include "qcustomwidget.h"

#include <QStringList>
#include <QHeaderView>
#include <QHBoxLayout>
#include <QComboBox>
#include <QLineEdit>
#include <QMetaObject>
#include <QPushButton>
#include <QDebug>

const int MAX_COLUMN_SIZE = 4;

/**************************************************************************/
/* LineEditDelegate 代理 */
/**************************************************************************/
LineEditDelegate::LineEditDelegate(QObject* parent) : QStyledItemDelegate(parent)
{
    m_pTableWidget = qobject_cast<QTableWidget*>(parent);
}

LineEditDelegate::~LineEditDelegate()
{
}

QWidget* LineEditDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
    QLineEdit *editor = new QLineEdit(parent);
    return editor;
}

void LineEditDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const
{
    QString text = index.model()->data(index, Qt::UserRole).toString();
    double value = floor((text.toDouble() * pow(10, 3) + 0.5)) / pow(10, 3);

    // 单元格设置修改的值时,会修改Qt::EditRole角色存储的数据,通过下面的打印可以很清楚地证明这一点
    QString strText = QString::number(value, 'f', 3);
    m_pTableWidget->item(index.row(), index.column())->setText(strText);

    qDebug() << "setEditorData data(Qt::EditRole) = " << m_pTableWidget->item(index.row(), index.column())->data(Qt::EditRole);
    qDebug() << "setEditorData data(Qt::UserRole) = " << m_pTableWidget->item(index.row(), index.column())->data(Qt::UserRole);

    QLineEdit* pLineEdit = qobject_cast<QLineEdit*>(editor);
    pLineEdit->setText(text);
}

void LineEditDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const
{
    QLineEdit* pLineEdit = qobject_cast<QLineEdit*>(editor);
    QString text = pLineEdit->text();

    model->setData(index, text, Qt::UserRole);
}

/**************************************************************************/
/* ComboBoxDelegate 代理 */
/**************************************************************************/
ComboBoxDelegate::ComboBoxDelegate(QObject* parent) : QStyledItemDelegate(parent)
{
    m_pTableWidget = qobject_cast<QTableWidget*>(parent);
}

ComboBoxDelegate::~ComboBoxDelegate()
{
}

QWidget* ComboBoxDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
    QComboBox *editor = new QComboBox(parent);
    editor->addItem(QString::fromLocal8Bit("男"));
    editor->addItem(QString::fromLocal8Bit("女"));
    editor->setCurrentIndex(0);
    return editor;
}

void ComboBoxDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const
{
    QComboBox* pComboBox = qobject_cast<QComboBox*>(editor);
    QString text = index.model()->data(index, Qt::EditRole).toString();

    int nIndex = pComboBox->findText(text);
    pComboBox->setCurrentIndex(nIndex);

    return;
}

void ComboBoxDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const
{
    QComboBox* pComboBox = qobject_cast<QComboBox*>(editor);
    QString text = pComboBox->currentText();

    model->setData(index, text, Qt::EditRole);
}

/**************************************************************************/
/* QMyTableWidget 类实现 */
/**************************************************************************/
QMyTableWidget::QMyTableWidget(QWidget *parent) : QTableWidget(parent)
{

}

QMyTableWidget::QMyTableWidget(int rows, int columns, QWidget *parent) : QTableWidget(rows, columns, parent)
{

}

QModelIndex QMyTableWidget::GetIndexFromItem(QTableWidgetItem *item) const
{
    return indexFromItem(item);
}

/**************************************************************************/
/* QCustomWidget 类实现 */
/**************************************************************************/
void QCustomWidget::OnCliecked()
{
//    QTableWidgetItem *item = m_pTableWidget->item(1, 1);
//    item->setData(Qt::EditRole, QVariant(QString("123")));

//    QModelIndex index = m_pTableWidget->GetIndexFromItem(item);
//    QVariant data = index.model()->data(index, Qt::EditRole);

//    qDebug() << data.toString();
}


QCustomWidget::QCustomWidget(QWidget *parent) : QWidget(parent)
{
    // 设置QWidget对应的背景色
    setStyleSheet("background-color:yellow;");

    Init();
}

QCustomWidget::~QCustomWidget()
{
    qDebug() << "QCustomWidget Destroy";
}

void QCustomWidget::Init()
{
    CreateWidgets();

    CreateLayout();

    InitTableData();

    InitTableContent();

    InitTableStyle();

    InitTableDelegate();

    connect(m_pPushButton, SIGNAL(clicked()), this, SLOT(OnCliecked()));
    connect(m_pTableWidget, SIGNAL(cellChanged(int, int)), this, SLOT(OnCellChanged(int, int)));
    connect(m_pTableWidget, SIGNAL(cellDoubleClicked(int, int)), this, SLOT(OnCellDoubleClicked(int, int)));
}

void QCustomWidget::InitTableStyle()
{
    // 设置表格占满父窗口
    m_pTableWidget->setAutoFillBackground(true);
    // 设置表格行选中
//    m_pTableWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
    // 表格大小自适应父窗口变化
    m_pTableWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    // 设置每列的宽度自适应窗口变化
    m_pTableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
    // 设置行表头不能被选中
    m_pTableWidget->horizontalHeader()->setSelectionMode(QAbstractItemView::NoSelection);
    // 隐藏列表头
    m_pTableWidget->verticalHeader()->hide();
    // 设置不显示表格线
//    m_pTableWidget->setShowGrid(false);
    // 设置右键下拉菜单样式
    m_pTableWidget->setContextMenuPolicy(Qt::CustomContextMenu);
}

void QCustomWidget::InitTableData()
{
    RowData data1 = {QString("Jams"), QString("20"), QString::fromLocal8Bit("男"), QString("123456789")};
    RowData data2 = {QString("Messi"), QString("20"), QString::fromLocal8Bit("男"), QString("123456789")};
    RowData data3 = {QString("Luccy"), QString("20"), QString::fromLocal8Bit("男"), QString("123456789")};

    m_rowData.push_back(data1);
    m_rowData.push_back(data2);
    m_rowData.push_back(data3);
}

void QCustomWidget::InitTableContent()
{
    for (int row = 0; row < m_rowData.size(); row ++)
    {
        // 注意:如果不设置行数,表格的内容显示不出来
        m_pTableWidget->setRowCount(m_pTableWidget->rowCount() + 1);

        for (int col = 0; col < MAX_COLUMN_SIZE; col ++)
        {
            QTableWidgetItem* pTableWidgetItemItem = new QTableWidgetItem;
            switch(col)
            {
            case 0:
                pTableWidgetItemItem->setText(m_rowData[row].name);
                break;
            case 1:
                pTableWidgetItemItem->setText(m_rowData[row].age);
                break;
            case 2:
                pTableWidgetItemItem->setText(m_rowData[row].sex);
                break;
            case 3:
                pTableWidgetItemItem->setText(m_rowData[row].number);
                break;
            default:
                break;
            }
            m_pTableWidget->setItem(row, col, pTableWidgetItemItem);
        }
    }
}

void QCustomWidget::InitTableDelegate()
{
    m_pTableWidget->setItemDelegateForColumn(1, new LineEditDelegate(m_pTableWidget));
    m_pTableWidget->setItemDelegateForColumn(2, new ComboBoxDelegate(m_pTableWidget));
}

void QCustomWidget::CreateWidgets()
{
    // 创建按键
    m_pPushButton = new QPushButton(QString::fromLocal8Bit("调试"), this);
    m_pPushButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);

    // 创建QTableWidget
    m_pTableWidget = new QMyTableWidget(0, 4, this);

    // 设置表头
    QStringList labels;
    labels << QString::fromLocal8Bit("姓名");
    labels << QString::fromLocal8Bit("年龄");
    labels << QString::fromLocal8Bit("性别");
    labels << QString::fromLocal8Bit("手机号");
    m_pTableWidget->setHorizontalHeaderLabels(labels);
}

void QCustomWidget::CreateLayout()
{
    QVBoxLayout *pLayout = new QVBoxLayout();

    pLayout->addWidget(m_pTableWidget);
    pLayout->addWidget(m_pPushButton);

    setLayout(pLayout);
}

void QCustomWidget::OnCellChanged(int row, int column)
{
    qDebug() << "CellChanged row = " << row << ", column = " << column;
}

void QCustomWidget::OnCellDoubleClicked(int row, int column)
{
    qDebug() << "CellDoubleClicked row = " << row << ", column = " << column;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值