Qt之QTableView、QTableWidget

提示:此文简单介绍了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

  参考链接

  • 2
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值