QTableView合并单元格及常见用法合并单元格函数原型及说明

在 Qt 中,QTableView 是一个非常强大的表格控件。本文将详细介绍如何在 QTableView 中合并单元格,并解决鼠标点击合并单元格后高亮显示的问题。

1合并单元格函数原型及说明

在 QTableView 中合并单元格需要自定义 QAbstractItemModel 的 span 函数。span 函数定义如下:

virtual QSize QAbstractItemModel::span(const QModelIndex &index) const;

此函数用于返回指定单元格的跨度(合并尺寸),返回的 QSize 表示从该单元格开始的行跨度和列跨度。

自定义表格模型:

首先,我们需要创建一个自定义的表格模型,继承自 QAbstractTableModel 或 QStandardItemModel,然后重载 span 函数。例如:

2具体效果展示

3代码展示

main.cpp

#include <QApplication>
#include "mainwindow.h"

int main(int argc, char *argv[]) {
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QTableView>
#include "fruitmodel.h"
#include "fruitdelegate.h"

class MainWindow : public QMainWindow {
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    QTableView *tableView;
    FruitModel *model;
    FruitDelegate *delegate;
};

#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include <QHeaderView>
#include <QVBoxLayout>
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent),
      tableView(new QTableView(this)),
      model(new FruitModel(this)),
      delegate(new FruitDelegate(this)) {
    QVBoxLayout *layout = new QVBoxLayout;
            layout->addWidget(tableView);
            QWidget *container = new QWidget;
            container->setLayout(layout);
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
        setCentralWidget(container);
#else
            layout->setContentsMargins(0, 0, 0, 0);  // 可选的,去除边距
            setLayout(layout);
#endif
            tableView->setModel(model);
            tableView->setItemDelegate(delegate);

            // 添加水果数据
//            Fruit fruit1("苹果", QString::fromLocal8Bit("中国"), 10, 20, 5, QIcon("../Data/china.png"));
//            Fruit fruit2("苹果", QString::fromLocal8Bit("日本"), 8, 15, 3, QIcon("../Data/japan.png"));
//            Fruit fruit3("苹果", QString::fromLocal8Bit("美国"), 12, 25, 6, QIcon("../Data/usa.png"));
//            Fruit fruit4("香蕉", QString::fromLocal8Bit("巴西"), 15, 25, 10,QIcon("../Data/brazil.png"));
//            Fruit fruit5("香蕉", QString::fromLocal8Bit("菲律宾"), 10, 20, 8, QIcon("../Data/philippines.png"));
//            Fruit fruit6("香蕉", QString::fromLocal8Bit("厄瓜多尔"), 12, 18, 7,QIcon("../Data/ecuador.png"));
//            Fruit fruit7("西瓜", QString::fromLocal8Bit("中国"), 20, 30, 10, QIcon("../Data/china.png"));
//            Fruit fruit8("西瓜", QString::fromLocal8Bit("美国"), 18, 28, 12, QIcon("../Data/usa.png"));
            Fruit fruit1("苹果", "中国", 10, 20, 5, QIcon("../Data/china.png"));
            Fruit fruit2("苹果", "日本", 8, 15, 3, QIcon("../Data/japan.png"));
            Fruit fruit3("苹果", "美国", 12, 25, 6, QIcon("../Data/usa.png"));
            Fruit fruit4("香蕉", "巴西", 15, 25, 10,QIcon("../Data/brazil.png"));
            Fruit fruit5("香蕉", "菲律宾", 10, 20, 8, QIcon("../Data/philippines.png"));
            Fruit fruit6("香蕉", "厄瓜多尔", 12, 18, 7,QIcon("../Data/ecuador.png"));
            Fruit fruit7("西瓜", "中国", 20, 30, 10, QIcon("../Data/china.png"));
            Fruit fruit8("西瓜", "美国", 18, 28, 12, QIcon("../Data/usa.png"));

            model->addFruit(fruit1);
            model->addFruit(fruit2);
            model->addFruit(fruit3);
            model->addFruit(fruit4);
            model->addFruit(fruit5);
            model->addFruit(fruit6);
            model->addFruit(fruit7);
            model->addFruit(fruit8);

            // 动态分配单元格
            QString currentFruit;
            int startRow = 0;
            for (int row = 0; row < model->rowCount(); ++row) {
                QString fruitName = model->data(model->index(row, 0)).toString();
                if (fruitName != currentFruit) {
                    if (!currentFruit.isEmpty()) {
                        tableView->setSpan(startRow, 0, row - startRow, 1);
                    }
                    currentFruit = fruitName;
                    startRow = row;
                }
            }
            // 最后一种水果的单元格合并
            tableView->setSpan(startRow, 0, model->rowCount() - startRow, 1);

            // 设置表格自适应
    #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
            tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
            tableView->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
    #else
            tableView->horizontalHeader()->setResizeMode(QHeaderView::Stretch);
            tableView->verticalHeader()->setResizeMode(QHeaderView::ResizeToContents);
    #endif
}

MainWindow::~MainWindow() {
}

fruitmodel.h

#ifndef FRUITMODEL_H
#define FRUITMODEL_H

#include <QAbstractTableModel>
#include <QList>
#include <QMap>
#include <QIcon>

struct Fruit {
    QString name;
    QString origin;
    int largeQuantity;
    int mediumQuantity;
    int smallQuantity;
    QIcon icon;
    Fruit(const QString &name,
            const QString &origin,
            int largeQuantity,
            int mediumQuantity,
            int smallQuantity,
            const QIcon &icon)
            : name(name),
            origin(origin),
            largeQuantity(largeQuantity),
            mediumQuantity(mediumQuantity),
            smallQuantity(smallQuantity),
            icon(icon) {}
};

class FruitModel : public QAbstractTableModel {
    Q_OBJECT

public:
    explicit FruitModel(QObject *parent = nullptr);
    void addFruit(const Fruit &fruit);

    int rowCount(const QModelIndex &parent = QModelIndex()) const override;
    int columnCount(const QModelIndex &parent = QModelIndex()) const override;
    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
    QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;

private:
    QList<Fruit> fruits;
};

#endif // FRUITMODEL_H

fruitmodel.cpp

#include "fruitmodel.h"
#include <QStringList>

FruitModel::FruitModel(QObject *parent)
    : QAbstractTableModel(parent) {
}

void FruitModel::addFruit(const Fruit &fruit) {
    beginInsertRows(QModelIndex(), fruits.count(), fruits.count());
    fruits.append(fruit);
    endInsertRows();
}

int FruitModel::rowCount(const QModelIndex &parent) const {
    Q_UNUSED(parent);
    return fruits.count();
}

int FruitModel::columnCount(const QModelIndex &parent) const {
    Q_UNUSED(parent);
    return 3; // 水果型号, 产地, 品质数量
}

QVariant FruitModel::data(const QModelIndex &index, int role) const {
    if (!index.isValid()) {
        return QVariant();
    }

    const Fruit &fruit = fruits.at(index.row());

    if (role == Qt::DisplayRole) {
        switch (index.column()) {
        case 0: // 水果型号
            return fruit.name;
        case 1: // 产地
            return fruit.origin;
        case 2: // 品质数量
            return QString("大: %1 \n中: %2 \n小: %3")
                .arg(fruit.largeQuantity)
                .arg(fruit.mediumQuantity)
                .arg(fruit.smallQuantity);
        }
    } else if (role == Qt::DecorationRole && index.column() == 1) {
        return fruit.icon;
    }

    return QVariant();
}

QVariant FruitModel::headerData(int section, Qt::Orientation orientation, int role) const {
    if (role == Qt::DisplayRole) {
        if (orientation == Qt::Horizontal) {
            switch (section) {
            case 0:
                return "水果型号";
            case 1:
                return "产地";
            case 2:
                return "品质数量";
            }
        }
    }
    return QVariant();
}

fruitdelegate.h

#ifndef FRUITDELEGATE_H
#define FRUITDELEGATE_H

#include <QStyledItemDelegate>

class FruitDelegate : public QStyledItemDelegate {
    Q_OBJECT

public:
    explicit FruitDelegate(QObject *parent = nullptr);

    void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
    QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
};

#endif // FRUITDELEGATE_H

fruitdelegate.cpp

#include "fruitdelegate.h"
#include <QPainter>
#include <QApplication>
#include <QTextOption>

FruitDelegate::FruitDelegate(QObject *parent)
    : QStyledItemDelegate(parent) {
}

void FruitDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const {
    // 调用基类的paint方法,确保其他列正常绘制
    QStyledItemDelegate::paint(painter, option, index);

    // 判断是否是第3列(品质数量)
    if (index.column() == 2) { 
        // 获取当前单元格的数据并转换为字符串
        QString text = index.data().toString();     
        // 将字符串按空格分割为多个部分
        QStringList lines = text.split(" ");  
        // 获取单元格的矩形区域
        QRect rect = option.rect;
        // 保存当前的画笔状态
        painter->save();
        // 将画笔的原点移动到矩形的左上角
        painter->translate(rect.topLeft());
        // 逆时针旋转90度
        painter->rotate(-90);
        // 调整矩形的大小,使其适应旋转后的内容
        rect.setSize(rect.size().transposed());
        // 设置文本选项,使文本居中对齐
        QTextOption textOption;
        textOption.setAlignment(Qt::AlignCenter);
        // 在旋转后的矩形中绘制文本,将分割的行用换行符连接
        painter->drawText(rect, lines.join("\n"), textOption);  
        // 恢复画笔的状态
        painter->restore();
    }
}

QSize FruitDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const {
    QSize size = QStyledItemDelegate::sizeHint(option, index);
    if (index.column() == 2) { // 品质数量
        size.setHeight(option.fontMetrics.height() * 3); // 调整高度以适应垂直文本
    }
    return size;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值