QTableWidget 详细使用指南

目录

      • 前言
      • 1 初始化表格
        • 1.1 设置表头
        • 1.2 设置列尺寸模式
      • 2 表格数据
        • 2.1 插入数据
        • 2.2 QTableWidgetItem 详解
      • 3 设置表格样式
        • 3.1 设置表格背景
        • 3.2 设置表头样式
        • 3.3 设置单元格样式
        • 3.4 设置选中行样式
        • 3.5 设置边框样式
        • 3.5 设置交替背景色
        • 3.6 设置单元格边框颜色
        • 3.7 设置单元格边框隐藏
        • 3.8 合并单元格
      • 4 与表格交互
        • 4.1 选择
        • 4.3 编辑
        • 4.5 排序
        • 4.6 删除行
        • 4.6 复制一行
        • 4.7 设置单元格Widget
      • 5 设置代理
      • 总结

前言

表格控件几乎是每个QT应用程序都会用到的控件之一,在QT框架中有两个表格组件一个是QTableWidget,另一个是QTableView,本文从实践出发详细介绍了QTableWidget的使用细节,包括表头设置、表格属性、添加数据、处理用户交互以及其他一些有用的技巧。

1 初始化表格

开发时一般在QT Designer 中拖拽一个QTableWidget到页面上,设置好布局后在UI页面对应的cpp文件中对表格进行操作。

1.1 设置表头
	QStringList headerList;
    headerList << QString::fromLocal8Bit("学号")
               << QString::fromLocal8Bit("姓名")
               << QString::fromLocal8Bit("语文")
               << QString::fromLocal8Bit("数学")
               << QString::fromLocal8Bit("英语") ;

    ui->tableWidget->setColumnCount(headerList.size());
    ui->tableWidget->setHorizontalHeaderLabels(headerList);

效果如下
QTableWidget 详细使用指南_第1张图片

可以看到上图中最后一列后面还有空白未分配,通过下面的设置可以使表头填满页面宽度。

1.2 设置列尺寸模式

通过下面的代码设置列宽模式。列尺寸模式在qt中是个枚举类型,如下:

enum ResizeMode
    {
        Interactive,
        Stretch,
        Fixed,
        ResizeToContents,
        Custom = Fixed
    };

下面分别介绍各个值对表头的影响。

  • Interactive ,交互模式,通过鼠标拖拽表格中间的分割线调整表格宽度。
ui->tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive);

QTableWidget 详细使用指南_第2张图片

  • Stretch ,使每一列都相等,并占满表格宽度。
 ui->tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);

QTableWidget 详细使用指南_第3张图片

  • Fixed 固定模式,这个模式一般与其他属性一起设置,比如手动设置列的宽度,设置最后一列自动拉伸。
    ui->tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed);
    ui->tableWidget->setColumnWidth(0, 30);
    ui->tableWidget->horizontalHeader()->setStretchLastSection(true);

QTableWidget 详细使用指南_第4张图片

  • ResizeToContents ,列的宽度会根据列标题文字的宽度设置
    ui->tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
    ui->tableWidget->horizontalHeader()->setStretchLastSection(true);

QTableWidget 详细使用指南_第5张图片

以上列宽模式也可以混合使用,如已经设置了Stretch模式,但是觉得某一列宽度不合适,可以按照如下方式进行设置。

    ui->tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
    ui->tableWidget->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Fixed);
    ui->tableWidget->setColumnWidth(0, 30);

如果不设置第0列为QHeaderView::Fixed,则setColumnWidth(0, 30); 将不起作用。

2 表格数据

2.1 插入数据

假设有一个表示学生成绩信息的数据结构如下:

typedef struct st_Student
{
    QString stuNo;
    QString name;
    double  chinese;
    double  math;
    double  english;

}stStudent;

QList<stStudent> stuInfoList 

stuInfoList 中有5条数据,将数据添加到表格中:

foreach (stStudent studentInfo, stuInfoList)
{
    setRowData(studentInfo);
}
    
void setRowData(const stStudent &stuInfo)
{
    QStringList dataList;
    dataList << stuInfo.stuNo << stuInfo.name << QString::number(stuInfo.chinese, 'f',2)
             << QString::number(stuInfo.math, 'f',2)<< QString::number(stuInfo.english, 'f',2);

	//获取表格中数据的条数,作为新插入的行号
    int row = ui->tableWidget->rowCount();
    //从表格最后新增一行
    ui->tableWidget->insertRow(row);
    for (int col = 0; col < dataList.size(); ++col)
    {
        QTableWidgetItem *pItem = new QTableWidgetItem(dataList[col]);
        ui->tableWidget->setItem(row, col, pItem);
    }
}

QTableWidget 详细使用指南_第6张图片

2.2 QTableWidgetItem 详解

QTableWidgetItem 是显示和编辑单元格数据的类。它可以包含文本、图标和数据,并提供了一系列方法来设置和获取这些数据。以下是一些 QTableWidgetItem 的主要特点和用法:

  • 构造函数:
    QTableWidgetItem 提供了多个构造函数,可以根据需要传递不同的参数。最常见的构造函数是接受一个字符串作为参数的构造函数,用于设置单元格的文本值:
QTableWidgetItem *item = new QTableWidgetItem("Hello");
  • 设置和获取文本值
item->setText("Hello");
QString text = item->text();
  • 设置和获取图标
QIcon icon(":/images/icon.png");
item->setIcon(icon);
QIcon itemIcon = item->icon();
  • 设置和获取数据:
    QTableWidgetItem 可以保存自定义的数据:
stStudent stuInfo;
QVariant data;
data.setValue(stuInfo);
item->setData(Qt::UserRole, data); // 保存自定义类型数据
QVariant data = item->data(Qt::UserRole);
stStudent value = data.value<stStudent>();
  • 设置文本对齐:
item->setTextAlignment(Qt::AlignCenter); // 文本居中对齐
Qt::Alignment alignment = item->textAlignment();

除了AlignCenter外还有 AlignLeft 、AlignRight 、AlignTop、AlignBottom、AlignVCenter等。

  • 设置和获取编辑属性:
    可以使用 setFlags(Qt::ItemFlags) 方法设置 QTableWidgetItem 的编辑属性
item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEditable); // 允许选择和编辑
Qt::ItemFlags itemFlags = item->flags();
  • 其他属性和方法:
    QTableWidgetItem 还提供了许多其他属性和方法,如设置前景色、背景色、字体、工具提示等。你可以根据需要查阅 Qt 的官方文档来了解更多详细信息。
 void setBackground(const QBrush &brush)//设置背景色
 void setCheckState(Qt::CheckState state)//设置CheckBox选中状态
 void setFont(const QFont &font)//设置字体
 void setForeground(const QBrush &brush)//设置前景色
 void setSelected(bool select)//设置是否选中
 void setSizeHint(const QSize &size)//设置尺寸大小
 void setToolTip(const QString &toolTip)//设置提示信息,当鼠标移到表格时自动显示该信息

总之,QTableWidgetItem 是在表格控件中用于显示和编辑单元格数据的重要类。它提供了丰富的功能和方法,可以根据需要设置和获取文本、图标、数据等属性,以满足表格数据的定制化需求。

3 设置表格样式

本节将结合样式表和表格属性来设置表格的样式,下图是QtDesigner中QTableWidget的属性列表
QTableWidget 详细使用指南_第7张图片

3.1 设置表格背景

这里把表格背景设置为浅灰色

tableWidget->setStyleSheet("QTableWidget { background-color: lightgray; }");

QTableWidget 详细使用指南_第8张图片

3.2 设置表头样式

使用 QHeaderView 和 ::section 选择器来设置表头的样式,例如设置背景色和字体样式:

tableWidget->horizontalHeader()->setStyleSheet("QHeaderView::section { background-color: gray; color: white; font-weight: bold; }");

QTableWidget 详细使用指南_第9张图片

3.3 设置单元格样式

使用 QTableWidget::item 选择器来设置单元格的样式,例如设置背景色和字体样式:

tableWidget->setStyleSheet("QTableWidget::item { background-color: lightblue; font-weight: bold; }");

QTableWidget 详细使用指南_第10张图片

3.4 设置选中行样式

使用 QTableWidget::item:selected 选择器来设置选中行的样式,例如设置背景色和字体颜色

tableWidget->setStyleSheet("QTableWidget::item:selected { background-color: blue; color: white; }");

QTableWidget 详细使用指南_第11张图片

3.5 设置边框样式

使用 border 属性来设置表格的边框样式,例如设置边框宽度和颜色:

tableWidget->setStyleSheet("QTableWidget { border: 1px solid black; }");

QTableWidget 详细使用指南_第12张图片

3.5 设置交替背景色

设置交替背景色时有两点需要注意,一是需要设置属性alternatingRowColors为true;二是不要设置单元格的背景色,即样式表中不要在有这样的设置QTableWidget::item { background-color: lightblue; }

tableWidget->setAlternatingRowColors(true);
tableWidget->setStyleSheet("QTableWidget { alternate-background-color: lightblue; }");

QTableWidget 详细使用指南_第13张图片

3.6 设置单元格边框颜色

使用 gridline-color 属性来设置表格的边框样式,例如设置边框宽度和颜色:

tableWidget->setStyleSheet("QTableWidget { gridline-color: green; }");

QTableWidget 详细使用指南_第14张图片

3.7 设置单元格边框隐藏
tableWidget->setShowGrid(false);

QTableWidget 详细使用指南_第15张图片

3.8 合并单元格

假设在学生成绩表中最后一行需要统计各学科的总分数,可以用下面的代码设置合并单元格:

    int row = ui->tableWidget->rowCount();
    ui->tableWidget->insertRow(row);

    QTableWidgetItem *pItem = new QTableWidgetItem("各科总分");
    pItem->setTextAlignment(Qt::AlignCenter);

    ui->tableWidget->setItem(row, 0, pItem);
    ui->tableWidget->setSpan(row, 0, 1, 2);

setSpan函数原型 void setSpan(int row, int column, int rowSpan, int columnSpan); 其中row表示行,column表示列,rowSpan 为行跨度,这里是1,columnSpan 为列跨度,这里是2。
QTableWidget 详细使用指南_第16张图片

4 与表格交互

4.1 选择

表格的选择效果一般需要selectionBehavior 和 selectionMode 这两个属性搭配设置,可以通过函数调用setSelectionBehaviorsetSelectionMode 设置,也可以直接在Designer中设置。

SelectionBehavior 是枚举变量其可选值有3个,其含义为:

  • SelectItems 0 按单元格选择,根据SelectionMode的值不同,可以选择一个或多个单元格。
  • SelectRows 1 按一行选择,根据SelectionMode的值不同,可以选择一行或多行。
  • SelectColumns 2 按列选择,根据SelectionMode的值不同,可以选择一列或多列。

SelectionMode 也是枚举变量其可选值有5个,其含义为:

  • NoSelection 0 不能选择任何单元格。
  • SingleSelection 1 只能选择一个单元格或者一行或者一列,选中一个后之前选中的会自动取消。
  • MultiSelection 2 当用户以普通方式选择一个单元格时,该单元格的选择状态将改变,而其他单元格将保持不变。用户可以通过拖动鼠标来选择多个单元格。
  • ExtendedSelection 3 当用户以普通方式选择一个单元格时,选择会被清除并选择新的项目。然而,如果用户在点击一个项目时按下 Ctrl 键,被点击的单元格会切换选择状态,而其他单元格保持不变。如果用户在点击一个单元格时按住 Shift 键,当前单元格和点击的单元格之间的所有单元格(包括当前单元格和点击的单元格)将根据点击的单元格的状态被选择或取消选择。用户可以通过拖动鼠标来选择多个单元格。
  • ContiguousSelection 4 当用户以普通方式选择一个单元格时,选择会被清除并选择新的单元格。然而,如果用户在点击一个单元格时按下 Shift 键,那么当前单元格和点击的单元格之间的所有单元格(包括当前单元格和点击的单元格)将根据点击的单元格的状态被选择或取消选择。

下面是几个示例

  • 只选一行
tableWidget->setSelectionMode(QAbstractItemView::SingleSelection);
tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows); 

QTableWidget 详细使用指南_第17张图片

  • 只选一列
tableWidget->setSelectionMode(QAbstractItemView::SingleSelection);
tableWidget->setSelectionBehavior(QAbstractItemView::SelectColumns); 

QTableWidget 详细使用指南_第18张图片

  • 选择任意多个连续的单元格
tableWidget->setSelectionMode(QAbstractItemView::MultiSelection);
tableWidget->setSelectionBehavior(QAbstractItemView::SelectItems); 

QTableWidget 详细使用指南_第19张图片

4.3 编辑

默认情况下,用户可以通过双击单元格来编辑其中的文本内容。也可以使用 setEditTriggers(QAbstractItemView::EditTriggers) 方法来设置编辑触发方式,如果编辑后的数据需要保存,还需处理数据保存逻辑:

tableWidget->setEditTriggers(QAbstractItemView::DoubleClicked);
4.5 排序

可以通过设置SortingEnabled属性为true,使其支持排序,通过点击表头,实现按照列进行升序降序排序。

  ui->tableWidget->setSortingEnabled(true);

QTableWidget 详细使用指南_第20张图片
QTableWidget 详细使用指南_第21张图片

还可以在页面中增加一个排序按钮,点击按钮后调用sortItems函数对某一列进行排序。

void slotSortBtnClicked()
{
	  int column = 0;  // 以第一列作为排序依据
	  Qt::SortOrder order = Qt::AscendingOrder;  // 升序排序
	  tableWidget->sortItems(column, order);
}

QTableWidget 详细使用指南_第22张图片

4.6 删除行

在页面中增加一个删除按钮,选中要删除的行,点击按钮后调用slotDeleteRow函数删除对应的一行。

void slotDeleteRow()
{
	int selectedRow = tableWidget->currentRow();
	if (selectedRow >= 0)
	{
	    tableWidget->removeRow(selectedRow);
	}
}

QTableWidget 详细使用指南_第23张图片

4.6 复制一行

在页面中增加一个复制按钮,选中要复制的行,点击按钮后调用copyRow函数复制当前选中行并将其添加到表格最后一行。

void copyRow()
{
     int selectedRow = tableWidget->currentRow();
     if (selectedRow >= 0)
     {
         int targetRow = tableWidget->rowCount(); // 目标位置为当前行数
         tableWidget->insertRow(targetRow); // 插入新行
         for (int column = 0; column < tableWidget->columnCount(); ++column)
         {
             QTableWidgetItem *sourceItem = tableWidget->item(selectedRow, column);
             if (sourceItem)
             {
                 QTableWidgetItem *targetItem = new QTableWidgetItem(*sourceItem);
                 tableWidget->setItem(targetRow, column, targetItem); // 复制数据到目标行
             }
         }
     }
}

QTableWidget 详细使用指南_第24张图片

4.7 设置单元格Widget

在上面的表格中新增一列“操作”,将删除和复制两个按钮添加到操作列中。修改表头设置代码:

	QStringList headerList;
    headerList << QString::fromLocal8Bit("学号")
               << QString::fromLocal8Bit("姓名")
               << QString::fromLocal8Bit("语文")
               << QString::fromLocal8Bit("数学")
               << QString::fromLocal8Bit("英语") ;
               << QString::fromLocal8Bit("操作") ;

    ui->tableWidget->setColumnCount(headerList.size());
    ui->tableWidget->setHorizontalHeaderLabels(headerList);

修改加载数据代码,在最后一列添加删除和复制按钮:

void setRowData(const stStudent &stuInfo)
{
    QStringList dataList;
    dataList << stuInfo.stuNo << stuInfo.name << QString::number(stuInfo.chinese, 'f',2)
             << QString::number(stuInfo.math, 'f',2)<< QString::number(stuInfo.english, 'f',2);

    int row = ui->tableWidget->rowCount();
    ui->tableWidget->insertRow(row);
    int col = 0;
    for (; col < dataList.size(); ++col)
    {
        QTableWidgetItem *pItem = new QTableWidgetItem(dataList[col]);
        pItem->setTextAlignment(Qt::AlignCenter);

        ui->tableWidget->setItem(row, col, pItem);
    }

    QWidget *pOperWidget = new QWidget();
    QHBoxLayout *pHBoxLayout = new QHBoxLayout();
    pOperWidget->setLayout(pHBoxLayout);
    pHBoxLayout->addSpacerItem(new QSpacerItem(1,1,QSizePolicy::Expanding));

    QPushButton *pCopyBtn = new QPushButton(QString::fromLocal8Bit("复制"));
    connect(pCopyBtn, SIGNAL(clicked()), this, SLOT(slotCopyRowBtnClicked()));
    pHBoxLayout->addWidget(pCopyBtn);

    QPushButton *pDelBtn = new QPushButton(QString::fromLocal8Bit("删除"));
    connect(pDelBtn, SIGNAL(clicked()), this, SLOT(slotDeleteRowBtnClicked()));
    pHBoxLayout->addWidget(pDelBtn);

    pHBoxLayout->setMargin(0);
    ui->tableWidget->setCellWidget(row, col, pOperWidget);
}

复制和删除按钮的槽函数也要做相应调整:

void slotDeleteRow()
{
	QPoint pos = QCursor::pos();
    pos = ui->tableWidget->viewport()->mapFromGlobal(pos);
    QModelIndex index = ui->tableWidget->indexAt(pos);

    int selectedRow = index.row();
	if (selectedRow >= 0)
	{
	    ui->tableWidget->removeRow(selectedRow);
	}
}

void copyRow()
{
     QPoint pos = QCursor::pos();
    pos = ui->tableWidget->viewport()->mapFromGlobal(pos);
    QModelIndex index = ui->tableWidget->indexAt(pos);

    int selectedRow = index.row();
    if (selectedRow >= 0)
    {
        int targetRow = ui->tableWidget->rowCount()-1; // 目标位置为当前行数
        ui->tableWidget->insertRow(targetRow); // 插入新行
        int column = 0;
        for (; column < ui->tableWidget->columnCount()-1; ++column)
        {
            QTableWidgetItem *sourceItem = ui->tableWidget->item(selectedRow, column);
            if (sourceItem)
            {
                QTableWidgetItem *targetItem = new QTableWidgetItem(*sourceItem);
                ui->tableWidget->setItem(targetRow, column, targetItem); // 复制数据到目标行
            }
        }

        QWidget *pOperWidget = new QWidget();
        QHBoxLayout *pHBoxLayout = new QHBoxLayout();
        pOperWidget->setLayout(pHBoxLayout);
        pHBoxLayout->addSpacerItem(new QSpacerItem(1,1,QSizePolicy::Expanding));

        QPushButton *pCopyBtn = new QPushButton(QString::fromLocal8Bit("复制"));
        connect(pCopyBtn, SIGNAL(clicked()), this, SLOT(slotCopyRowBtnClicked()));
        pHBoxLayout->addWidget(pCopyBtn);

        QPushButton *pDelBtn = new QPushButton(QString::fromLocal8Bit("删除"));
        connect(pDelBtn, SIGNAL(clicked()), this, SLOT(slotDeleteRowBtnClicked()));
        pHBoxLayout->addWidget(pDelBtn);

        pHBoxLayout->setMargin(0);
        ui->tableWidget->setCellWidget(targetRow, column, pOperWidget);
    }
}

QTableWidget 详细使用指南_第25张图片

5 设置代理

QItemDelegate 类是 Model/View 类之一,属于 Qt 的模型/视图框架的一部分,实际使用时一般继承QStyledItemDelegate来实现自定义代理。QTableWidget 的 ItemDelegate 可用于自定义单元格的显示和编辑方式,可以实现以下功能:

  • 自定义单元格的显示方式:可以自定义单元格中的文本、图像、背景颜色等。
  • 自定义单元格的编辑方式:可以定义不同类型的编辑器,如文本框、下拉框等,并控制编辑器的行为和验证规则。
  • 处理单元格的用户交互事件:可以捕获和处理单元格的鼠标点击、键盘输入等事件。

下面是一个QTableWidget设置代理的示例,自定义第一列和第二列单元格的显示和编辑方式:

class CustomDelegate : public QStyledItemDelegate
{
public:
    // 重写绘制函数,自定义单元格的显示方式
    void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;

    // 重写创建编辑器的函数,自定义单元格的编辑方式
    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;
};

void CustomDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    // 获取单元格的数据
    QVariant data = index.data(Qt::DisplayRole);
    // 设置背景颜色
    painter->fillRect(option.rect, Qt::yellow);
    // 绘制文本
    painter->drawText(option.rect, Qt::AlignCenter, data.toString());
}

QWidget *CustomDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    // 创建编辑器
    if (index.column() == 0)
    {
        QLineEdit *editor = new QLineEdit(parent);
        editor->setAlignment(Qt::AlignCenter);
        editor->setValidator(new QIntValidator(editor)); // 仅允许输入整数
        return editor;
    }
    else if (index.column() == 1)
    {
        QComboBox *editor = new QComboBox(parent);
        editor->addItem("Option 1");
        editor->addItem("Option 2");
        editor->addItem("Option 3");
        return editor;
    }

    return QStyledItemDelegate::createEditor(parent, option, index);
}

void CustomDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
    QVariant data = index.data(Qt::DisplayRole);

    if (QLineEdit *lineEdit = qobject_cast<QLineEdit *>(editor))
    {
        lineEdit->setText(data.toString());
    }
    else if (QComboBox *comboBox = qobject_cast<QComboBox *>(editor))
    {
        comboBox->setCurrentText(data.toString());
    }
    else
    {
        QStyledItemDelegate::setEditorData(editor, index);
    }
}

void CustomDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
    if (QLineEdit *lineEdit = qobject_cast<QLineEdit *>(editor))
    {
        model->setData(index, lineEdit->text(), Qt::EditRole);
    }
    else if (QComboBox *comboBox = qobject_cast<QComboBox *>(editor))
    {
        model->setData(index, comboBox->currentText(), Qt::EditRole);
    }
    else
    {
        QStyledItemDelegate::setModelData(editor, model, index);
    }
}

QTableWidget 详细使用指南_第26张图片

总结

本文详细介绍了实际开发过程中QTableWidget 的一些使用细节,提供了许多实用方法和功能示例,读者可以根据实际需求进行进一步的探索和使用。

你可能感兴趣的:(C++,c++,qt,QTableWidget)