五十六、QT之表格表头单元格合并,单元格内容只读和自定义样式

一、效果

在这里插入图片描述

二、基本思想

  由于无法直接操作表格的 header,所以只能采用一个 QTableWidgetQTableView 组合来实现一个表格, QTableWidget 用来实现表头,QTableView 用来加载数据。

注意:一旦设置代理后,选中行之后,背景色无法高亮!!
解决办法:把代理去掉,文字居中的话,采用 item->setTextAlignment(Qt::AlignCenter)

(一)自定义TableView

CustomTableView.h

#ifndef CUSTOMTABLEVIEW_H
#define CUSTOMTABLEVIEW_H

#include <QTableWidget>
#include <QMap>

#define HEADER_ROW_COUNT 2  //表头行数
#define HEADER_ROW_HEIGHT 42        //表头行高

class CustomTableView : public QTableView
{
public:
    CustomTableView(int columnCount, std::function<void (QTableWidget *)> addTableHeader, std::function<void (QTableView *)> setColumnsNoEdit, QWidget *parent = Q_NULLPTR);
    ~CustomTableView();

    void setColumnRowCounts(int columnCount, int rowCount); //设置表格内容几行几列

protected:
    //重载 resize事件,更新 headerTableWidget 位置
    void resizeEvent(QResizeEvent *event) Q_DECL_OVERRIDE;

    //重载 headerTableWidget 移动事件
    void scrollTo(const QModelIndex &index, ScrollHint hint) Q_DECL_OVERRIDE;

    //冲在虚函数 鼠标移动事件
    QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers) Q_DECL_OVERRIDE;

private:
    QTableWidget *headerTableWidget;            //自定义表头,实现表头单元格合并
    int columnCount;                            //表格有几列
    int rowCount;                               //表格内容有几行

private:
    //初始化表头(PS:表头的具体几行几列合并,由调用方决定)
    void initTableHeader(std::function<void(QTableWidget *headerTableWidget)> addTableHeader);
    //初始化显示内容的 TableView(PS:哪些列不可编辑,由调用方决定)
    void initTableContent(std::function<void(QTableView *tableView)> setColumnsNoEdit);
    void updateHeaderTableGeometry();  //更新表头的位置

};

#endif // CUSTOMTABLEVIEW_H

CustomTableView.cpp

#include "customtableview.h"

#include <QHeaderView>
#include <QScrollBar>

#include "itemdelegatealigncenter.h"

CustomTableView::CustomTableView(int columnCount, std::function<void (QTableWidget *)> addTableHeader,
                                 std::function<void (QTableView *)> setColumnsNoEdit, QWidget *parent) : QTableView(parent)
{
    this->columnCount = columnCount;
    initTableHeader(addTableHeader);
    initTableContent(setColumnsNoEdit);
    //添加表格数据
}

CustomTableView::~CustomTableView()
{
    delete headerTableWidget;
}

void CustomTableView::setColumnRowCounts(int columnCount, int rowCount)
{
    this->columnCount = columnCount;
    this->rowCount = rowCount;
}

void CustomTableView::resizeEvent(QResizeEvent *event)
{
    QAbstractItemView::resizeEvent(event);
    updateHeaderTableGeometry();
}

void CustomTableView::scrollTo(const QModelIndex &index, QAbstractItemView::ScrollHint hint)
{
    if (index.row() > 0) {
        QTableView::scrollTo(index, hint);
    }
}

//看不懂这个函数是啥意思
QModelIndex CustomTableView::moveCursor(QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
{
    QModelIndex currentModelIndex = QTableView::moveCursor(cursorAction, modifiers);
    if (cursorAction == QAbstractItemView::MoveUp && currentModelIndex.row() > 0
            && this->visualRect(currentModelIndex).topLeft().y() < headerTableWidget->rowHeight(1)) {
        int newValue = this->verticalScrollBar()->value() + this->visualRect(currentModelIndex).topLeft().y()
                - headerTableWidget->rowHeight(0) - headerTableWidget->rowHeight(1);
        this->verticalScrollBar()->setValue(newValue);
    }
    return currentModelIndex;
}

void CustomTableView::initTableHeader(std::function<void (QTableWidget *)> addTableHeader)
{
    headerTableWidget = new QTableWidget(this);
    headerTableWidget->horizontalHeader()->setVisible(false);
    headerTableWidget->verticalHeader()->setVisible(false);

//    headerTableWidget->setShowGrid(false);                                        //网格线不可见
    headerTableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);          //设置单元格不可编辑
    headerTableWidget->horizontalHeader()->setStretchLastSection(true);             //最后一个单元格扩展
    headerTableWidget->setFocusPolicy(Qt::NoFocus);                                 //解决选中虚框问题
    headerTableWidget->setFrameShape(QFrame::NoFrame);                              //去除边框(QTableView 默认去除边框,若这里不去除,则内容和表头无法对齐)
    headerTableWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);          //隐藏垂直滚动条
    headerTableWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);        //隐藏水平滚动条
    headerTableWidget->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);  //一次滚动一个像素

    headerTableWidget->setItemDelegate(new ItemDelegateAlignCenter(true, this));                //设置绘画代理(若表头有特殊样式要求,可以在这里绘制出来)

    this->viewport()->stackUnder(headerTableWidget); //设置窗口层次,表头始终在内容表格上面

    headerTableWidget->setColumnCount(this->columnCount); //表头几列
    headerTableWidget->setRowCount(HEADER_ROW_COUNT);     //表头几行

    for (int i=0; i<HEADER_ROW_COUNT; i++) {
        headerTableWidget->setRowHeight(0, HEADER_ROW_HEIGHT); //设置行高
    }

    //隐藏2行后的行(因为 TableWidget 默认会有一些空行存在)
    for (int i=HEADER_ROW_COUNT; i<headerTableWidget->rowCount(); i++) {
        headerTableWidget->setRowHidden(i, true);
    }

    //设置表头内容和格式(该方法已经抽象,由调用方决定)
    addTableHeader(headerTableWidget);
    //表头水平滚动条滚动,表格内容也随之滚动
    connect(headerTableWidget->horizontalScrollBar(), SIGNAL(valueChanged(int)),
            this->horizontalScrollBar(), SLOT(setValue(int)));
    //表格内容水平滚动条滚动,表头也随之滚动
    connect(this->horizontalScrollBar(), SIGNAL(valueChanged(int)),
            headerTableWidget->horizontalScrollBar(), SLOT(setValue(int)));
    updateHeaderTableGeometry();  //更新位置
    headerTableWidget->show();    //显示
}

void CustomTableView::initTableContent(std::function<void (QTableView *)> setColumnsNoEdit)
{
    this->horizontalHeader()->setVisible(true);  //表头可见(留给 headerTableWidget 遮挡,否则就会遮挡表格内容)

    //设置表头高度,和 headerTableWidget 总高度一致
    int headerHeight = 0;
    for (int i=0; i<HEADER_ROW_COUNT; i++) {
        headerHeight += headerTableWidget->rowHeight(i);
    }
    this->horizontalHeader()->setFixedHeight(headerHeight);

    this->verticalHeader()->setVisible(false);
//    this->setShowGrid(false);   //网格线不可见
//    this->setEditTriggers(QAbstractItemView::NoEditTriggers);

    //设置哪些列不可编辑
    setColumnsNoEdit(this);

    this->setSelectionMode(QAbstractItemView::SingleSelection);   //单选
    this->setSelectionBehavior(QAbstractItemView::SelectRows);    //选行
    this->horizontalHeader()->setStretchLastSection(true);        //最后一个单元格扩展
    this->setFocusPolicy(Qt::NoFocus);  //解决选中虚框问题
//    this->setFrameShape(QFrame::NoFrame); //去除边框

    this->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);   //一次滚动一个像素
    this->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); //一次滚动一个像素

    this->setItemDelegate(new ItemDelegateAlignCenter(false, this));  //设置绘画代理(若表格有特殊样式要求,可以在这里绘制出来)
}

void CustomTableView::updateHeaderTableGeometry()
{
    headerTableWidget->setGeometry(this->frameWidth(), this->frameWidth(), this->viewport()->width(),
                                   this->horizontalHeader()->height());
}

(二)单元格文字居中代理

ItemDelegateAlignCenter.h

#ifndef ITEMDELEGATEALIGNCENTER_H
#define ITEMDELEGATEALIGNCENTER_H

#include <QStyledItemDelegate>

class ItemDelegateAlignCenter : public QStyledItemDelegate
{
public:
    ItemDelegateAlignCenter(bool isHead, QObject *parent = 0);
    void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE;

private:
    bool isHead;  //是不是表头
};

#endif // ITEMDELEGATEALIGNCENTER_H

ItemDelegateAlignCenter.cpp

#include "itemdelegatealigncenter.h"

#include <QTextOption>
#include <QPainter>

ItemDelegateAlignCenter::ItemDelegateAlignCenter(bool isHead, QObject *parent) :
    isHead(isHead),
    QStyledItemDelegate(parent)
{

}

void ItemDelegateAlignCenter::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    //若是表头,设置单元格背景色
    if (isHead) {
        QColor color(148, 169, 205);
        painter->fillRect(option.rect, color);
    }
    //单元格内文字居中
    QTextOption op;
    op.setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);

    //设置字体
    QFont font;
    font.setFamily("Microsoft YaHei");
    font.setPixelSize(14);
//    font.setBold(true);
    painter->setFont(font);

    //单元格文字居左
//    QRect rect;
//    rect = QRect(option.rect.x(), option.rect.y(), 100, option.rect.height());
//    painter->drawText(rect, index.data(Qt::DisplayRole).toString(), op);

    painter->drawText(option.rect, index.data(Qt::DisplayRole).toString(), op);
}

(三)单元格只读代理

ItemDelegateReadOnly.h

#ifndef ITEMDELEGATEREADONLY_H
#define ITEMDELEGATEREADONLY_H

#include <QItemDelegate>

class ItemDelegateReadOnly : public QItemDelegate
{
public:
    ItemDelegateReadOnly(QWidget *parent = 0);

protected:
    QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE;
    void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE;
};

#endif // ITEMDELEGATEREADONLY_H

ItemDelegateReadOnly.cpp

#include "itemdelegatereadonly.h"

#include <QTextOption>
#include <QPainter>

ItemDelegateReadOnly::ItemDelegateReadOnly(QWidget *parent) : QItemDelegate(parent)
{

}

QWidget *ItemDelegateReadOnly::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    //单元格只读,不可编辑

    Q_UNUSED(parent);
    Q_UNUSED(option);
    Q_UNUSED(index);
    return NULL;
}

void ItemDelegateReadOnly::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    //文字居中

    QTextOption op;
    op.setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);

    //设置字体
    QFont font;
    font.setFamily("Microsoft YaHei");
    font.setPixelSize(14);
//    font.setBold(true);
    painter->setFont(font);

    painter->drawText(option.rect, index.data(Qt::DisplayRole).toString(), op);
}

(四)使用案例

Widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QStandardItemModel>
#include <QItemSelectionModel>
#include "customtableview.h"

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();

private:
    Ui::Widget *ui;
    CustomTableView *tableView;
    QStandardItemModel *model;
    QItemSelectionModel *selectionModel;
};

#endif // WIDGET_H

Widget.cpp

#include "widget.h"
#include "ui_widget.h"

#include <QTableWidget>
#include <QPushButton>
#include <QHBoxLayout>
#include "itemdelegatereadonly.h"

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);

    tableView = new CustomTableView(11, [](QTableWidget *headerTable){
            //设置header内容
            headerTable->setSpan(0, 0, 2, 1);
            headerTable->setSpan(0, 1, 2, 1);
            headerTable->setSpan(0, 2, 2, 1);
            headerTable->setSpan(0, 3, 1, 3);
            headerTable->setSpan(0, 6, 1, 4);
            headerTable->setSpan(0, 10, 2, 1);

            headerTable->setItem(0, 0, new QTableWidgetItem("表头1"));
            headerTable->setItem(0, 1, new QTableWidgetItem("表头2"));
            headerTable->setItem(0, 2, new QTableWidgetItem("表头3"));
            headerTable->setItem(0, 3, new QTableWidgetItem("表头4"));
            headerTable->setItem(0, 6, new QTableWidgetItem("表头5"));
            headerTable->setItem(0, 10, new QTableWidgetItem("表头6"));
            headerTable->setItem(1, 3, new QTableWidgetItem("表头7"));
            headerTable->setItem(1, 4, new QTableWidgetItem("表头8"));
            headerTable->setItem(1, 5, new QTableWidgetItem("表头9"));
            headerTable->setItem(1, 6, new QTableWidgetItem("表头10"));
            headerTable->setItem(1, 7, new QTableWidgetItem("表头11"));
            headerTable->setItem(1, 8, new QTableWidgetItem("表头12"));
            headerTable->setItem(1, 9, new QTableWidgetItem("表头13"));

        }, [this](QTableView *contentTable){
            ItemDelegateReadOnly *itemDelegate = new ItemDelegateReadOnly(this);
            contentTable->setItemDelegateForColumn(0, itemDelegate);
            contentTable->setItemDelegateForColumn(1, itemDelegate);
            contentTable->setItemDelegateForColumn(2, itemDelegate);
        }, this);

    model = new QStandardItemModel(this);
    model->setRowCount(1);
    model->setColumnCount(11);

    selectionModel = new QItemSelectionModel(model);

    tableView->setModel(model);
    tableView->setSelectionModel(selectionModel);

    QStandardItem *item;

    item = new QStandardItem("1");
    model->setItem(0, 0, item);

    item = new QStandardItem("1");
    model->setItem(0, 1, item);

    item = new QStandardItem("1");
    model->setItem(0, 2, item);

    item = new QStandardItem("3544973");
    model->setItem(0, 3, item);

    item = new QStandardItem("20699057");
    model->setItem(0, 4, item);

    item = new QStandardItem("42");
    model->setItem(0, 5, item);

    item = new QStandardItem("25000");
    model->setItem(0, 6, item);

    item = new QStandardItem("3000");
    model->setItem(0, 7, item);

    item = new QStandardItem("-400");
    model->setItem(0, 8, item);

    item = new QStandardItem("400");
    model->setItem(0, 9, item);

    item = new QStandardItem("11-11-11");
    model->setItem(0, 10, item);

    //设置水平布局,表格铺满控件
    QHBoxLayout *layout = new QHBoxLayout(this);
    layout->addWidget(tableView);
}

Widget::~Widget()
{
    delete ui;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值