QTableView
阅读数:307 评论数:0
跳转到新版页面分类
C/C++
正文
一、概述
QTableView
是 Qt 中一个功能强大的表格显示控件,它依赖于模型-视图-委托(Model-View-Delegate)架构。QTableView
提供了灵活的数据展示和编辑功能,适用于复杂的数据表格展示需求。
二、基本使用步骤
1、创建并配置 QTableView
。
2、创建并配置数据模型(如 QStandardItemModel
)。
3、将数据模型设置为 QTableView
的模型。
4、(可选)自定义视图外观和行为。
示例
#include <QApplication>
#include <QTableView>
#include <QStandardItemModel>
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
// 创建 QTableView 实例
QTableView tableView;
tableView.setWindowTitle("QTableView Example");
// 创建模型
QStandardItemModel model(5, 3); // 5行3列
model.setHorizontalHeaderLabels({"Column 1", "Column 2", "Column 3"});
// 填充数据
for (int row = 0; row < 5; ++row) {
for (int col = 0; col < 3; ++col) {
QStandardItem *item = new QStandardItem(QString("Item %1,%2").arg(row).arg(col));
model.setItem(row, col, item);
}
}
// 将模型设置为 QTableView 的模型
tableView.setModel(&model);
// 显示 QTableView
tableView.show();
return app.exec();
}
三、自定义外观
1、设置列宽
tableView.setColumnWidth(0, 150); // 设置第一列宽度为150像素
2、设置选择模式
tableView.setSelectionMode(QAbstractItemView::SingleSelection); // 仅允许单选
tableView.setSelectionBehavior(QAbstractItemView::SelectRows); // 选择整行
tableWidget.setSelectionMode(QTableWidget::MultiSelection); // 多行选择
设置行选中的背景色和字体颜色
// 设置选中行的背景色和字体颜色
tableView.setStyleSheet("QTableView::item:selected { background-color: #3399FF; color: white; }");
3、隐藏网格线
tableView.setShowGrid(false);
4、设置排序功能
tableView.setSortingEnabled(true);
5、自定义委托
你可以自定义委托来控制单元格的显示和编辑行为。以下是一个简单的示例,使用 QStyledItemDelegate
来自定义单元格的显示:
class CustomDelegate : public QStyledItemDelegate {
public:
CustomDelegate(QObject *parent = nullptr) : QStyledItemDelegate(parent) {}
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override {
// 自定义绘制代码
painter->save();
painter->setPen(Qt::blue);
painter->drawText(option.rect, Qt::AlignCenter, index.data().toString());
painter->restore();
}
};
// 使用自定义委托
tableView.setItemDelegate(new CustomDelegate(&tableView));
6、隐藏行号(如果不需要默认的行号)
tableView.verticalHeader()->setVisible(false); // 隐藏默认行号
7、设置表头样式
tableView.setStyleSheet(
"QHeaderView::section {"
" background-color: #4CAF50;" // 表头背景色
" color: white;" // 表头字体颜色
" font-size: 14px;" // 表头字体大小
" font-weight: bold;" // 表头字体加粗
" height: 30px;" // 表头高度
" border: none;" // 表头去掉边框
"}"
"QTableView::item:selected {"
" background-color: #FF9800;" // 选中行背景色
" color: white;" // 选中行字体颜色
"}"
);
8、默认选中第一行
// 设置整行选择
tableView.setSelectionBehavior(QTableView::SelectRows);
// 设置单行选择模式
tableView.setSelectionMode(QTableView::SingleSelection);
// 默认选中第一行
QModelIndex firstIndex = model.index(0, 0); // 获取第一行的第一个单元格的索引
tableView.setCurrentIndex(firstIndex); // 设置当前选中的索引
tableView.selectRow(0);
四、QStandardItemModel
QStandardItemModel
是一种数据模型,用于存储和管理二维表格数据。与许多其他 Qt 对象不同,QStandardItemModel
的构造函数不需要指定父对象(parent
)。这是因为数据模型通常由视图(如 QTableView
、QTreeView
等)持有,而视图本身会负责管理模型的生命周期。
void loadModelFromFile(QStandardItemModel *model, const QString &fileName) {
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
qWarning("Cannot open file for reading");
return;
}
QTextStream in(&file);
bool isFirstLine = true;
while (!in.atEnd()) {
QString line = in.readLine();
QStringList fields = line.split(',');
if (isFirstLine) {
// 设置表头
model->setHorizontalHeaderLabels(fields);
isFirstLine = false;
} else {
// 添加数据行
QList<QStandardItem *> items;
for (const QString &field : fields) {
items.append(new QStandardItem(field.trimmed()));
}
model->appendRow(items);
}
}
file.close();
}
#include <QApplication>
#include <QTableView>
#include <QStandardItemModel>
#include <QFile>
#include <QTextStream>
#include <QDebug>
void saveModelToFile(QStandardItemModel *model, const QString &fileName) {
QFile file(fileName);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
qWarning("Cannot open file for writing");
return;
}
QTextStream out(&file);
// 写入表头
QStringList headerLabels;
for (int col = 0; col < model->columnCount(); ++col) {
headerLabels << model->headerData(col, Qt::Horizontal).toString();
}
out << headerLabels.join(",") << "\n";
// 写入数据行
for (int row = 0; row < model->rowCount(); ++row) {
QStringList rowValues;
for (int col = 0; col < model->columnCount(); ++col) {
QStandardItem *item = model->item(row, col);
if (item) {
rowValues << item->text();
} else {
rowValues << "";
}
}
out << rowValues.join(",") << "\n";
}
file.close();
}
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
// 创建 QTableView 和 QStandardItemModel
QTableView tableView;
QStandardItemModel model(5, 3); // 5 行 3 列
// 填充一些示例数据
for (int row = 0; row < 5; ++row) {
for (int col = 0; col < 3; ++col) {
QStandardItem *item = new QStandardItem(QString("Item %1,%2").arg(row).arg(col));
model.setItem(row, col, item);
}
}
tableView.setModel(&model);
tableView.show();
// 保存模型数据到文件
QString fileName = "output.csv"; // 请替换为你的文件路径
saveModelToFile(&model, fileName);
return app.exec();
}
五、实现搜索功能
在 QTableView
中实现搜索功能可以通过过滤模型(如 QSortFilterProxyModel
)来实现。QSortFilterProxyModel
可以过滤和排序数据,并将结果显示在 QTableView
中。
#include <QApplication>
#include <QTableView>
#include <QStandardItemModel>
#include <QSortFilterProxyModel>
#include <QLineEdit>
#include <QVBoxLayout>
#include <QWidget>
#include <QHeaderView>
class MainWindow : public QWidget {
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr) : QWidget(parent) {
QVBoxLayout *layout = new QVBoxLayout(this);
// 创建 QTableView 和 QStandardItemModel
QTableView *tableView = new QTableView(this);
QStandardItemModel *model = new QStandardItemModel(5, 4, this); // 5 行 4 列
// 填充一些示例数据
for (int row = 0; row < 5; ++row) {
for (int col = 0; col < 4; ++col) {
QStandardItem *item = new QStandardItem(QString("Item %1,%2").arg(row).arg(col));
model->setItem(row, col, item);
}
}
// 创建 QSortFilterProxyModel
QSortFilterProxyModel *proxyModel = new QSortFilterProxyModel(this);
proxyModel->setSourceModel(model);
proxyModel->setFilterKeyColumn(-1); // -1 表示所有列都进行过滤
tableView->setModel(proxyModel);
// 创建 QLineEdit 用于输入搜索关键字
QLineEdit *searchLineEdit = new QLineEdit(this);
searchLineEdit->setPlaceholderText("Search...");
// 连接 QLineEdit 的文本变化信号到过滤模型的过滤器
connect(searchLineEdit, &QLineEdit::textChanged, proxyModel, &QSortFilterProxyModel::setFilterWildcard);
layout->addWidget(searchLineEdit);
layout->addWidget(tableView);
setLayout(layout);
setWindowTitle("QTableView Search Example");
resize(600, 400);
}
};
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
MainWindow mainWindow;
mainWindow.show();
return app.exec();
}
#include "main.moc"
六、最后一列为按钮列
#include <QStyledItemDelegate>
#include <QApplication>
#include <QMouseEvent>
#include <QPainter>
class MultiButtonDelegate : public QStyledItemDelegate {
Q_OBJECT
public:
MultiButtonDelegate(QObject *parent = nullptr) : QStyledItemDelegate(parent) {}
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override {
if (index.column() == index.model()->columnCount() - 1) {
int buttonWidth = option.rect.width() / 3;
QStyleOptionButton buttonOption1;
buttonOption1.rect = option.rect.adjusted(0, 0, -2 * buttonWidth, 0);
buttonOption1.text = "Button 1";
buttonOption1.state = QStyle::State_Enabled;
QStyleOptionButton buttonOption2;
buttonOption2.rect = option.rect.adjusted(buttonWidth, 0, -buttonWidth, 0);
buttonOption2.text = "Button 2";
buttonOption2.state = QStyle::State_Enabled;
QStyleOptionButton buttonOption3;
buttonOption3.rect = option.rect.adjusted(2 * buttonWidth, 0, 0, 0);
buttonOption3.text = "Button 3";
buttonOption3.state = QStyle::State_Enabled;
QApplication::style()->drawControl(QStyle::CE_PushButton, &buttonOption1, painter);
QApplication::style()->drawControl(QStyle::CE_PushButton, &buttonOption2, painter);
QApplication::style()->drawControl(QStyle::CE_PushButton, &buttonOption3, painter);
} else {
QStyledItemDelegate::paint(painter, option, index);
}
}
bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) override {
if (event->type() == QEvent::MouseButtonPress) {
QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
if (index.column() == index.model()->columnCount() - 1) {
int buttonWidth = option.rect.width() / 3;
QRect buttonRect1 = option.rect.adjusted(0, 0, -2 * buttonWidth, 0);
QRect buttonRect2 = option.rect.adjusted(buttonWidth, 0, -buttonWidth, 0);
QRect buttonRect3 = option.rect.adjusted(2 * buttonWidth, 0, 0, 0);
if (buttonRect1.contains(mouseEvent->pos())) {
emit button1Clicked(index);
} else if (buttonRect2.contains(mouseEvent->pos())) {
emit button2Clicked(index);
} else if (buttonRect3.contains(mouseEvent->pos())) {
emit button3Clicked(index);
}
}
}
return QStyledItemDelegate::editorEvent(event, model, option, index);
}
signals:
void button1Clicked(const QModelIndex &index) const;
void button2Clicked(const QModelIndex &index) const;
void button3Clicked(const QModelIndex &index) const;
};
七、使用校验器
默认情况下,QTableView
会在双击时启动一个编辑器(通常是一个 QLineEdit
),允许用户修改单元格内容。如果我们需要校验输入,完全可以利用 QTableView
的编辑机制。
在这种情况下,您只需要为 QTableView
的编辑控件(即 QLineEdit
)设置一个输入校验器。这样,QTableView
会自动处理编辑和校验输入,而无需手动管理编辑控件。
#include <QApplication>
#include <QTableView>
#include <QStandardItemModel>
#include <QStandardItem>
#include <QItemDelegate>
#include <QLineEdit>
#include <QDoubleValidator>
#include <QVBoxLayout>
#include <QWidget>
#include <QMessageBox>
class MyItemDelegate : public QItemDelegate {
Q_OBJECT
public:
MyItemDelegate(QObject *parent = nullptr) : QItemDelegate(parent) {}
// 重写 createEditor 方法,条件化地为某些列设置校验器
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override {
QLineEdit *editor = new QLineEdit(parent);
// 假设只对第1列和第2列应用校验器(列索引从0开始)
if (index.column() == 1 || index.column() == 2) {
// 创建一个校验器,允许整数或浮点数输入
QDoubleValidator *validator = new QDoubleValidator(editor);
validator->setBottom(0); // 设置最小值为0
editor->setValidator(validator);
}
return editor;
}
// 重写 setModelData 方法,确保输入合法时提交数据
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override {
QLineEdit *lineEdit = qobject_cast<QLineEdit*>(editor);
// 获取用户输入的内容
QString text = lineEdit->text();
// 如果输入为空且该列不允许空值,则弹出提示框并拒绝提交
if (text.isEmpty()) {
QMessageBox::warning(nullptr, "Invalid Input", "This field cannot be empty.");
return; // 不提交数据
}
// 如果输入合法且非空,则提交数据
if (lineEdit && lineEdit->hasAcceptableInput()) {
QItemDelegate::setModelData(editor, model, index); // 输入合法时提交数据
} else {
// 如果输入无效,什么都不做
// 可以在这里弹出错误提示或提示用户
qWarning() << "Invalid input!";
}
}
};
class MyTableView : public QWidget {
Q_OBJECT
public:
MyTableView(QWidget *parent = nullptr) : QWidget(parent) {
QTableView *tableView = new QTableView(this);
QStandardItemModel *model = new QStandardItemModel(5, 3, this);
// 填充一些初始数据
for (int row = 0; row < 5; ++row) {
for (int col = 0; col < 3; ++col) {
QStandardItem *item = new QStandardItem(QString("Item %1-%2").arg(row).arg(col));
model->setItem(row, col, item);
}
}
tableView->setModel(model);
// 设置自定义的委托
tableView->setItemDelegate(new MyItemDelegate(this));
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(tableView);
setLayout(layout);
}
};
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
MyTableView window;
window.resize(800, 600);
window.show();
return app.exec();
}
#include "main.moc"