提示:此文简单介绍了QTableWidget及QTableView的区别,以QTableWidget为例,讲述表格样式,并演示了表格样式静态和动态的效果。
前言
QTableWidget是QT中的表格组件类。一般用来展示多行多列的数据,是QT中使用较多的控件之一。
QTableWidget继承于类QTableView。
一、QTableView和QTableWidget的区别
两者主要区别是QTableView可以使用自定义的数据模型来显示内容(也就意味着使用时先要通过setModel来绑定数据源),而QTableWidget则只能使用标准的数据模型。
理论上来讲,用QTableView实现的功能,在QTableWidget中也能达到同样的效果,因为QTableWidget它是继承自[public] QTableView,接口都是通用的。
QTableWidget封装了非常完善的事件反应接口,但本质是同QTableView一致,只不过QTableWidget生产了一些扩展的代码。
Qt中的表格部件由QTableWidget、QTableView实现,详情参见下表:
类名 | 描述 |
QHeaderView | 表格的表头部件,直接继承自QAbstractItemView, 但我们关注的是它继承自 QWidget |
QSqlTableModel | 表格的数据库数据模型,直接继承自QSqlQueryModel, 但我们关注的是它继承自 QAbstractItemModel |
QStandardItemModel | 表格的标准数据模型,直接继承自QAbstractItemModel |
QScrollArea | 表格的数据渲染区域(工作区),直接继承自QWidget |
QStandardItem | 表格的单元格渲染部件,是一个纯数据结构体的存在,不接入Qt框架中 |
在这里我们将表格功能模块按照MVC的设计模式来理解。因为Qt在这个功能模块中使用的就是这种模式而实现的。
M: 模型,QAbstractItemModel 的实例化类对象,负责数据的处理,如数据库数据更新或者删除时对数据的处理单元。
V: 视图,QHeaderView 或者 QTableWidget 实例对象,负责渲染数据模型的数据到界面。同时承担与用户交互的角色。
C: 控制器,我的理解是Qt的信号和槽机制(元对象系统)承担控制器的角色,但这里是直发,其内部可能会做出一些提高用户体验的优化。
Qt表格功能模型示意图
二、QTableWidget的组成
表格部件的布局主要是有表头区域和数据渲染区域组成,表头作为导航的功能和作用,数据渲染区域是后台数据的渲染容器,提供界面渲染和与用户交互的作用。
如果我们需要自定义表头或者数据渲染容器样式,那么只需要设置表头部件或者数据渲染容器的QSS风格即可,如果仍然满足不了需求时,应该考虑自己扩展一个表格功能类。
三、QTableWidget的样式
QTableWidget的样式基本是可以完全参考自QWidget的QSS样式,但表格部件本身是有私人定制的QSS样式
// 表格部件的样式
QTableWidget
// 单元格样式
QTableWidget::item
// 每个单元格被选中的状态样式
QTableWidget::item::selected
// 垂直滚动条 handle
QScrollBar::handle:vertical
// 垂直滚动条 区域
QScrollBar::vertical
// 垂直滚动条 handle上、下区域(未被handle占用的区域
QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical
// 垂直滚动条 最顶端与最低端的区域
QScollBar::add-line:vertical, QScrollBar::sub-line:vertical
// 标题头的样式;每个单独的标题区域
QHeaderView::section
四、效果演示
静态:
动态:
五、代码
SampleQTableWidget.h
#ifndef SAMPLEQTABLEWIDGET_H
#define SAMPLEQTABLEWIDGET_H
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui { class SampleQTableWidget; }
QT_END_NAMESPACE
class QMovie;
class SampleQTableWidget : public QWidget
{
Q_OBJECT
public:
SampleQTableWidget(QWidget *parent = nullptr);
~SampleQTableWidget();
private:
QMovie* initializeQtDynamicPictureToCache(const QString& file, const QString& cache) const;
void updateDynamicPictureForNextFPS(QMovie*, QMovie*);
void updateHeaderDynamicPictureForNextFPS(QMovie*);
void updateViewPortDynamicPictureForNextFPS(QMovie*);
private:
Ui::SampleQTableWidget *ui;
};
#endif // SAMPLEQTABLEWIDGET_H
SampleQTableWidget.cpp
#include "sampleqtablewidget.h"
#include "ui_sampleqtablewidget.h"
#include <QDebug>
#define Q_SAMPLE_BACKGROUND_GIF
//#define Q_SAMPLE_BACKGROUND_STATIC_PICTURE
#if defined(Q_SAMPLE_BACKGROUND_GIF)
# include <QDir>
# include <QTimer>
# include <QMovie>
#endif
SampleQTableWidget::SampleQTableWidget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::SampleQTableWidget)
{
ui->setupUi(this);
this->ui->tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
this->ui->tableWidget->horizontalHeader()->setMinimumHeight(120);
this->ui->tableWidget->setRowCount(10);
#if defined(Q_SAMPLE_BACKGROUND_STATIC_PICTURE)
//QString url = QString("QWidget#%1{border-image: url(%2.png);}")
this->ui->tableWidget->horizontalHeader()->setStyleSheet("border-image: url(:/static/resources/static_byk_viewport_bk_01.jpg);");
QString url = QString("QWidget#%1{border-image: url(%2);}")
.arg(this->ui->tableWidget->viewport()->objectName())
.arg(":/static/resources/static_byk_viewport_bk_02.jpg");
this->ui->tableWidget->viewport()->setStyleSheet(url);
#endif
#if defined(Q_SAMPLE_BACKGROUND_GIF)
/**
Split the dynamic image into static multi frame images and cache them in the cache folder under the working directory.
*/
auto movie = this->initializeQtDynamicPictureToCache(":/dynamic/resources/dynamic_byk_viewport_bk_01.webp", "cache");
auto movie_header = this->initializeQtDynamicPictureToCache(":/dynamic/resources/dynamic_byk_viewport_bk_02.webp", "cache_header");
// The timer circularly updates the static picture of the corresponding frame cached by the dynamic picture to achieve
// the effect of static picture to dynamic picture.
QTimer* renderBkTimer = new QTimer();
connect(renderBkTimer, &QTimer::timeout, this, [=](){
this->updateDynamicPictureForNextFPS(movie_header, movie);
});
renderBkTimer->start(100);
#endif
}
SampleQTableWidget::~SampleQTableWidget()
{
delete ui;
}
QMovie* SampleQTableWidget::initializeQtDynamicPictureToCache(const QString &file, const QString &cache) const
{
#if defined(Q_SAMPLE_BACKGROUND_GIF)
QDir dir(QDir::currentPath());
if(dir.exists(cache))
{
dir.rmdir(cache);
}
dir.mkdir(cache);
dir.setPath(cache);
QMovie* movie = new QMovie(file);
movie->setCacheMode(QMovie::CacheAll);
for(auto index = 0; index < movie->frameCount(); ++index)
{
movie->jumpToFrame(index);
movie->currentImage().save(QString("%1/%2.png").arg(dir.absolutePath()).arg(index));
this->ui->textBrowser->append(QString("Expand [%1]: [%2] frame image to [%3]").arg(file).arg(index).arg(QString("%1/%2.png").arg(dir.absolutePath()).arg(index)));
}
return movie;
#else
Q_UNUSED(file)
Q_UNUSED(cache)
return nullptr;
#endif
}
void SampleQTableWidget::updateDynamicPictureForNextFPS(QMovie* header, QMovie* viewPort)
{
this->updateViewPortDynamicPictureForNextFPS(viewPort);
this->updateHeaderDynamicPictureForNextFPS(header);
}
void SampleQTableWidget::updateHeaderDynamicPictureForNextFPS(QMovie* movie)
{
#if defined(Q_SAMPLE_BACKGROUND_GIF)
if(nullptr == movie)
{
return;
}
static int index_header = 0;
if(index_header < movie->frameCount())
{
QString url = QString("border-image: url(%1.png);")
.arg(QDir::current().absoluteFilePath("cache_header/" + QString::number(index_header)));
this->ui->tableWidget->horizontalHeader()->setStyleSheet(url);
}
this->ui->textBrowser->append(QString("update piture index for header: %1").arg(index_header));
if(++index_header >= movie->frameCount())
{
index_header = 0;
}
#else
Q_UNUSED(movie)
#endif
}
void SampleQTableWidget::updateViewPortDynamicPictureForNextFPS(QMovie* movie)
{
#if defined(Q_SAMPLE_BACKGROUND_GIF)
if(nullptr == movie)
{
return;
}
static int index = 0;
if(index < movie->frameCount())
{
QString url = QString("QWidget#%1{border-image: url(%2.png);}")
.arg(this->ui->tableWidget->viewport()->objectName())
.arg(QDir::current().absoluteFilePath("cache/" + QString::number(index)));
this->ui->tableWidget->viewport()->setStyleSheet(url);
}
this->ui->textBrowser->append(QString("update piture index for viewport: %1").arg(index));
if(++index >= movie->frameCount())
{
index = 0;
}
#else
Q_UNUSED(movie)
#endif
}
总结
以上就是今天要讲的内容,本文主要展示了QTableWidget与QTableView的表格样式的应用。
示例项目下载地址:Qt/Widgets/sample_qtablewidget
参考链接