五十六、QT之表格表頭單元格合併,單元格內容只讀和自定義樣式

一、效果

在這裏插入圖片描述

二、基本思想

  由於無法直接操作表格的 header,所以只能採用一個 QTableWidgetQTableView 組合來實現一個表格, QTableWidget 用來實現表頭,QTableView 用來加載數據。

(一)自定義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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章