表格控件几乎是每个QT应用程序都会用到的控件之一,在QT框架中有两个表格组件一个是QTableWidget,另一个是QTableView,本文从实践出发详细介绍了QTableWidget的使用细节,包括表头设置、表格属性、添加数据、处理用户交互以及其他一些有用的技巧。
开发时一般在QT Designer 中拖拽一个QTableWidget到页面上,设置好布局后在UI页面对应的cpp文件中对表格进行操作。
QStringList headerList;
headerList << QString::fromLocal8Bit("学号")
<< QString::fromLocal8Bit("姓名")
<< QString::fromLocal8Bit("语文")
<< QString::fromLocal8Bit("数学")
<< QString::fromLocal8Bit("英语") ;
ui->tableWidget->setColumnCount(headerList.size());
ui->tableWidget->setHorizontalHeaderLabels(headerList);
可以看到上图中最后一列后面还有空白未分配,通过下面的设置可以使表头填满页面宽度。
通过下面的代码设置列宽模式。列尺寸模式在qt中是个枚举类型,如下:
enum ResizeMode
{
Interactive,
Stretch,
Fixed,
ResizeToContents,
Custom = Fixed
};
下面分别介绍各个值对表头的影响。
ui->tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive);
ui->tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
ui->tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed);
ui->tableWidget->setColumnWidth(0, 30);
ui->tableWidget->horizontalHeader()->setStretchLastSection(true);
ui->tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
ui->tableWidget->horizontalHeader()->setStretchLastSection(true);
以上列宽模式也可以混合使用,如已经设置了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); 将不起作用。
假设有一个表示学生成绩信息的数据结构如下:
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);
}
}
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();
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等。
item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEditable); // 允许选择和编辑
Qt::ItemFlags itemFlags = item->flags();
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 是在表格控件中用于显示和编辑单元格数据的重要类。它提供了丰富的功能和方法,可以根据需要设置和获取文本、图标、数据等属性,以满足表格数据的定制化需求。
本节将结合样式表和表格属性来设置表格的样式,下图是QtDesigner中QTableWidget的属性列表
这里把表格背景设置为浅灰色
tableWidget->setStyleSheet("QTableWidget { background-color: lightgray; }");
使用 QHeaderView 和 ::section 选择器来设置表头的样式,例如设置背景色和字体样式:
tableWidget->horizontalHeader()->setStyleSheet("QHeaderView::section { background-color: gray; color: white; font-weight: bold; }");
使用 QTableWidget::item 选择器来设置单元格的样式,例如设置背景色和字体样式:
tableWidget->setStyleSheet("QTableWidget::item { background-color: lightblue; font-weight: bold; }");
使用 QTableWidget::item:selected 选择器来设置选中行的样式,例如设置背景色和字体颜色
tableWidget->setStyleSheet("QTableWidget::item:selected { background-color: blue; color: white; }");
使用 border 属性来设置表格的边框样式,例如设置边框宽度和颜色:
tableWidget->setStyleSheet("QTableWidget { border: 1px solid black; }");
设置交替背景色时有两点需要注意,一是需要设置属性alternatingRowColors为true;二是不要设置单元格的背景色,即样式表中不要在有这样的设置QTableWidget::item { background-color: lightblue; }
。
tableWidget->setAlternatingRowColors(true);
tableWidget->setStyleSheet("QTableWidget { alternate-background-color: lightblue; }");
使用 gridline-color 属性来设置表格的边框样式,例如设置边框宽度和颜色:
tableWidget->setStyleSheet("QTableWidget { gridline-color: green; }");
tableWidget->setShowGrid(false);
假设在学生成绩表中最后一行需要统计各学科的总分数,可以用下面的代码设置合并单元格:
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。
表格的选择效果一般需要selectionBehavior 和 selectionMode 这两个属性搭配设置,可以通过函数调用setSelectionBehavior
、setSelectionMode
设置,也可以直接在Designer中设置。
SelectionBehavior 是枚举变量其可选值有3个,其含义为:
SelectionMode 也是枚举变量其可选值有5个,其含义为:
下面是几个示例
tableWidget->setSelectionMode(QAbstractItemView::SingleSelection);
tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
tableWidget->setSelectionMode(QAbstractItemView::SingleSelection);
tableWidget->setSelectionBehavior(QAbstractItemView::SelectColumns);
tableWidget->setSelectionMode(QAbstractItemView::MultiSelection);
tableWidget->setSelectionBehavior(QAbstractItemView::SelectItems);
默认情况下,用户可以通过双击单元格来编辑其中的文本内容。也可以使用 setEditTriggers(QAbstractItemView::EditTriggers) 方法来设置编辑触发方式,如果编辑后的数据需要保存,还需处理数据保存逻辑:
tableWidget->setEditTriggers(QAbstractItemView::DoubleClicked);
可以通过设置SortingEnabled属性为true,使其支持排序,通过点击表头,实现按照列进行升序降序排序。
ui->tableWidget->setSortingEnabled(true);
还可以在页面中增加一个排序按钮,点击按钮后调用sortItems函数对某一列进行排序。
void slotSortBtnClicked()
{
int column = 0; // 以第一列作为排序依据
Qt::SortOrder order = Qt::AscendingOrder; // 升序排序
tableWidget->sortItems(column, order);
}
在页面中增加一个删除按钮,选中要删除的行,点击按钮后调用slotDeleteRow函数删除对应的一行。
void slotDeleteRow()
{
int selectedRow = tableWidget->currentRow();
if (selectedRow >= 0)
{
tableWidget->removeRow(selectedRow);
}
}
在页面中增加一个复制按钮,选中要复制的行,点击按钮后调用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); // 复制数据到目标行
}
}
}
}
在上面的表格中新增一列“操作”,将删除和复制两个按钮添加到操作列中。修改表头设置代码:
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);
}
}
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 的一些使用细节,提供了许多实用方法和功能示例,读者可以根据实际需求进行进一步的探索和使用。