QT QTableView添加CheckBox

发布时间:2023年12月17日

原作:

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

看不懂,加了一些注释?

#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
#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);
}
#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
#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;
}
#include "mainwindow.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}
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

文章来源:https://blog.csdn.net/tankeven/article/details/134962243
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。