QT QTableView添加CheckBox

原作:

https://blog.csdn.net/lyq240919525/article/details/131748325

看不懂,加了一些注释

cpp 复制代码
#ifndef CHECKBOXDELETAGE_H
#define CHECKBOXDELETAGE_H
// `QStyledItemDelegate`是一个Qt标准项代理的基类,提供了一些默认的实现方法,它是实现代理模式的基础之一。
// 在`Qt`模型/视图框架中,代理模式用于将数据源(model)的数据呈现到视图(view)中,或者将用户界面中的数据更改反映回数据源。
// 通过继承`QStyledItemDelegate`类,我们可以实现重写方法来改变特定表项的渲染行为和呈现方式。
// 比如,我们可以用它来重新定义表单元格的字体、颜色、对齐方式等。
// 同时,通过自定义代理类,我们还可以实现对表单元格的交互方式进行改变,比如实现复选框或者下拉框选择等。
// `QStyledItemDelegate`类可以被继承或直接使用其方法,以实现您自定义的表单元格的渲染效果和编辑行为。
#include <QStyledItemDelegate>

// `class QPixmap;`是一个前置声明。通常在一个头文件中声明一个类,而在另一个头文件或者源文件中定义这个类。
// 在该头文件中使用前置声明可以有效地减少编译时的依赖性,从而提高编译速度,减少编译错误。
// 这里的`class QPixmap;`并不是定义`QPixmap`类的声明,而是为了告诉编译器`QPixmap`类在这里是一个类,
// 并且将在其他头文件或源文件中进行定义。因此,这种声明被称为"前置声明"。
class QPixmap;
class CheckBoxDelegate : public QStyledItemDelegate
{
    Q_OBJECT

public:
    enum class CheckBoxPaintType : quint8
    {
        SysControl  = 0, // 系统控件
        OwnerDraw   = 1, // 自绘CheckBox
        DrawImage   = 2, // 绘制图片
    };

public:
    explicit CheckBoxDelegate(CheckBoxPaintType type = CheckBoxPaintType::SysControl, QWidget *parent = nullptr);
    ~CheckBoxDelegate();

    void initCheckBoxPixmaps(CheckBoxPaintType type);
    void initCheckBoxPixmapBySysControl(const QString& key, bool checked);
    void initCheckBoxPixmapByOwnerDraw(const QString& key, bool checked);
    void initCheckBoxPixmapByDrawImage(const QString& key, bool checked);

    // 虚函数
    void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
    bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) override;

signals:

private:
    QPixmap* mCheckedPix = nullptr;
    QPixmap* mUnCheckedPix = nullptr;
};

#endif // CHECKBOXDELETAGE_H
cpp 复制代码
#include "checkboxdeletage.h"
#include <QCheckBox>
#include <QApplication>
#include <QMouseEvent>
#include <QPixmapCache>
#include <QPainter>
#include <QColor>

CheckBoxDelegate::CheckBoxDelegate(CheckBoxDelegate::CheckBoxPaintType type, QWidget *parent)
    : QStyledItemDelegate(parent)
{
    initCheckBoxPixmaps(type);
}

CheckBoxDelegate::~CheckBoxDelegate()
{
    delete mCheckedPix;
    delete mUnCheckedPix;
}

void CheckBoxDelegate::initCheckBoxPixmaps(CheckBoxDelegate::CheckBoxPaintType type)
{
    switch (type)
    {
    case CheckBoxPaintType::SysControl:
        initCheckBoxPixmapBySysControl("checked", true);
        initCheckBoxPixmapBySysControl("unchecked", false);
        break;
    case CheckBoxPaintType::OwnerDraw:
        initCheckBoxPixmapByOwnerDraw("checked", true);
        initCheckBoxPixmapByOwnerDraw("unchecked", false);
        break;
    case CheckBoxPaintType::DrawImage:
        initCheckBoxPixmapByDrawImage("checked", true);
        initCheckBoxPixmapByDrawImage("unchecked", false);
        break;
    default:
        break;
    }
}

void CheckBoxDelegate::initCheckBoxPixmapBySysControl(const QString &key, bool checked)
{
    (void)key;
    QPixmap* pix = new QPixmap(13,13);
    QPainter painter(pix);

    QStyleOptionToolButton style;
    style.iconSize = QSize(13, 13);
    style.state = checked ? QStyle::State_On : QStyle::State_Off;
    style.state |= QStyle::State_Enabled;
    style.rect = QRect(0,0, 13,13);
    QCheckBox box;
    QApplication::style()->drawPrimitive(QStyle::PE_IndicatorCheckBox, &style, &painter, &box);

    if (checked)
        mCheckedPix = pix;
    else
        mUnCheckedPix = pix;
}

void CheckBoxDelegate::initCheckBoxPixmapByOwnerDraw(const QString &key, bool checked)
{
    (void)key;
    // 在堆空间中动态地分配一个大小为13x13像素的新的`QPixmap`对象,并返回一个指向这个对象的指针。然后将这个指针存储在`pix`变量中。
    // `QPixmap`是一个表示一个图像的二维像素图的类。
    // 我们可以使用`QPixmap`对象在Qt应用程序中显示图像、进行图像编辑,或者在QPainter中绘制其它内容。
    // 在这个例子中,我们使用`QPixmap`对象来绘制复选框的图标,并利用这个图标来定制复选框的呈现和样式。
    QPixmap* pix = new QPixmap(13,13);
    QPainter painter(pix);
    painter.setPen(Qt::green);
    // 使用`QRect`定义了一个矩形区域,该矩形区域的左上角顶点(左上角坐标)位于`(0, 0)`,右下角顶点的坐标为`(13, 13)`。
    // 这样定义的矩形区域大小正好与`QPixmap`对象`pix`的大小相同。
    // 在后续代码中,我们可以利用这个矩形区域对`pix`对象进行裁剪或绘制操作。通常可以使用`QPixmap`对象`copy`函数截取一个矩形子图像,或者使用`QPainter`对象的绘图函数在`pix`对象上进行绘制。
    // 需要注意的是,`QRect`的坐标系与绘图坐标系不同。在矩形坐标系中,坐标原点为左上角`(0, 0)`,X轴正方向向右,Y轴正方向向下。
    // 而在绘图坐标系中,坐标原点为左下角`(0, 0)`,X轴正方向向右,Y轴正方向向上。因此,在使用`QRect`矩形区域时需要注意它所处的坐标系,以及使用它的目的。
    QRect rect(0,0,13,13);
    // 抗锯齿
    // `painter.setRenderHint(QPainter::Antialiasing)`开启了平滑抗锯齿渲染选项,可以使图形看起来更平滑和清晰。
    painter.setRenderHint(QPainter::Antialiasing);
    // `painter.setBrush(Qt::white)`设置当前绘制画刷为白色,画刷用于填充和着色图形。在这里,使用了`Qt::white`预定义的颜色值表示白色。
    painter.setBrush(Qt::white);
    // 使用`painter.drawRect(rect)`函数在画布的矩形区域`rect`上绘制矩形。这个矩形的边框颜色由画笔决定,前面已经设置为绿色。在矩形内部使用画刷填充颜色,也就是白色。
    painter.drawRect(rect);

    if (checked)
    {
        QPointF points[3] = {
            QPointF(rect.x() + rect.width() * 2 / 11, rect.y() + rect.height() * 6 / 11),
            QPointF(rect.x() + rect.width() * 4 / 11, rect.y() + rect.height() * 8 / 11),
            QPointF(rect.x() + rect.width() * 9 / 11, rect.y() + rect.height() * 3 / 11),
        };
        painter.setPen(QPen(QBrush(Qt::green),2));
        painter.drawPolyline(points, 3);
    }

    if (checked)
        mCheckedPix = pix;
    else
        mUnCheckedPix = pix;
    // 也可直接放入QPixmapCache中去,此处就简单处理更高效些
    //QPixmapCache::insert(key, pix);
}

void CheckBoxDelegate::initCheckBoxPixmapByDrawImage(const QString &key, bool checked)
{
    (void)key;
    QPixmap* pix = new QPixmap(checked ? ":/image/checked.png" : ":/image/unchecked.png");

    if (checked)
        mCheckedPix = pix;
    else
        mUnCheckedPix = pix;
}

// 重写`QStyledItemDelegate`类中的`paint`方法,并利用`QPixmap`对象作为复选框的显示图标。
void CheckBoxDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    QStyleOptionViewItem viewOption(option);
    initStyleOption(&viewOption, index);
    if (viewOption.state.testFlag(QStyle::State_HasFocus))
        viewOption.state ^= QStyle::State_HasFocus;

    QStyledItemDelegate::paint(painter, option, index);

    bool checked = index.data(Qt::UserRole).toBool();

    QPixmap* pix = checked? mCheckedPix : mUnCheckedPix;
    if (pix)
    {
        // 居中显示
        QRect rect = pix->rect();
        int offsetX = option.rect.width() / 2 - rect.width() / 2;
        int offsetY = option.rect.height() / 2 - rect.height() / 2;
        rect.translate(option.rect.x() + offsetX, option.rect.y() + offsetY);
        painter->drawPixmap(rect, *pix);
    }
}

bool CheckBoxDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index)
{
    if (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonDblClick)
    {
        // 鼠标单双击切换选中状态
        QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
        if (option.rect.contains(mouseEvent->pos()))
        {
            model->setData(index, !model->data(index, Qt::UserRole).toBool(), Qt::UserRole);
        }
    }
    return QStyledItemDelegate::editorEvent(event, model, option, index);
}
cpp 复制代码
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

private:
    Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
cpp 复制代码
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QTabWidget>
#include <QSplitter>
#include <QTableView>
#include <QStandardItemModel>
#include "checkboxdeletage.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    // `this`表示指向`MainWindow`对象的指针。
    // 在构造函数中,通过`this`指针,我们可以向窗口或控件添加子控件(如代码中的`QTabWidget`)。
    // 在`MainWindow`类的构造函数中,`this`指针指向当前`MainWindow`对象的地址,因为构造函数在创建新的`MainWindow`对象时被调用。
    // 在这段代码中,`this`指针用于实例化`QTabWidget`控件并将其添加为`MainWindow`对象的子控件。
    QTabWidget* tabWidget = new QTabWidget(this);
    // `Qt::Orientation::Horizontal`表示将`QSplitter`控件设置为水平方向,Orientation(方向)
    QSplitter* splitter = new QSplitter(Qt::Orientation::Horizontal, this);
    QTableView* tableView = new QTableView(this);
    // 此数据模型包含一个2x3的表格(2行,3列)
    QStandardItemModel* model = new QStandardItemModel(2,3,this);
    tableView->setModel(model);
    // 为`tableView`的第0列设置了一个代理对象(`CheckBoxDelegate`)。代理对象可以在QTableView中设置用于自定义单元格的渲染和编辑的控件或行为。
    // 第二个参数是代理对象的指针。在这里,使用了一个新的`CheckBoxDelegate`对象,
    // 并且构造函数中传递了`CheckBoxPaintType`枚举类型的值`SysControl`(表示使用系统默认的复选框样式)和`this`(哪个窗口控件拥有这个代理)。
    tableView->setItemDelegateForColumn(0, new CheckBoxDelegate(CheckBoxDelegate::CheckBoxPaintType::SysControl, this));
//    tableView->setItemDelegateForColumn(1, new CheckBoxDelegate(CheckBoxDelegate::CheckBoxPaintType::OwnerDraw, this));
//    tableView->setItemDelegateForColumn(2, new CheckBoxDelegate(CheckBoxDelegate::CheckBoxPaintType::DrawImage, this));
    tableView->setItemDelegateForColumn(1, new CheckBoxDelegate(CheckBoxDelegate::CheckBoxPaintType::OwnerDraw, this));
    tableView->setItemDelegateForColumn(2, new CheckBoxDelegate(CheckBoxDelegate::CheckBoxPaintType::OwnerDraw, this));

    // 不可编辑
    tableView->setEditTriggers(QAbstractItemView::EditTrigger::NoEditTriggers);
    // 整行选中
    tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
    // 多行可选
    tableView->setSelectionMode(QAbstractItemView::SelectionMode::ExtendedSelection);

    splitter->addWidget(tableView);
    tabWidget->addTab(splitter, "CheckBox");

    setCentralWidget(tabWidget);
}

MainWindow::~MainWindow()
{
    delete ui;
}
cpp 复制代码
#include "mainwindow.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}
cpp 复制代码
QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++11

# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS

# You can also make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

SOURCES += \
    checkboxdeletage.cpp \
    main.cpp \
    mainwindow.cpp

HEADERS += \
    checkboxdeletage.h \
    mainwindow.h

FORMS += \
    mainwindow.ui

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
相关推荐
isyangli_blog6 小时前
OpenDayLight (Carbon 版本) 启动与组件安装
开发语言·php
vb2008116 小时前
FastAPI APIRouter
开发语言·python
Benszen7 小时前
KVM虚拟化解决方案
开发语言·perl
会编程的土豆7 小时前
Go 语言反射(Reflection)详解
开发语言·后端·golang
東雪木7 小时前
多线程与并发编程 专属复习笔记
java·开发语言·笔记·java面试
杨充7 小时前
1.3 浮点型数据设计灵魂
开发语言·python·算法
噜噜噜阿鲁~7 小时前
python学习笔记 | 11.3、面向对象高级编程-多重继承
java·开发语言
basketball6167 小时前
Go 语言从入门到进阶:4. 数组和MAP使用方法总结
开发语言·后端·golang
春生野草8 小时前
反射、Tomcat执行
java·开发语言
雪的季节9 小时前
企业级 Qt 全功能项目
开发语言·数据库·qt