Qt 模型视图 - QTableWidget(2)

1 简述

QTableWidget 是 Qt 框架中用于显示二维表格数据的组件,表格中的每个单元格都是 QTableWidgetItem 对象,可以用它来设置和读取单元格中的内容和样式。

  • 在实际开发中常常需要通过自定义 QTableWidgetItem 来创造更加丰富多彩的单元格内容。
  • 简单易用、功能齐全以及可高度自定义的优点很适合处理中等规模的数据。

开发环境

  • 系统:Window10
  • Qt版本:5.14.2
  • 编译器:MinGW_64

2 实现效果

在这里插入图片描述

3 实现步骤

3.1 自定义CustomizeHeaderView类

这个类主要是在headerView上加一个QToolButton控件用来批量选中所有项以及绘制表项。
customizeheaderview.h文件

class CustomizeHeaderView : public QHeaderView
{
    Q_OBJECT
public:
    explicit CustomizeHeaderView(Qt::Orientation orientation, QWidget *parent = nullptr);

    void setHeaderViewWidget(int logicalIndex, QWidget *widget);

    void setSectionHidden(int logicalIndex, bool hide);

protected:
    virtual void paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const;

private:
    QMap<int, QWidget*> headerViewWidgets;
};

customizeheaderview.cpp源文件

void CustomizeHeaderView::setHeaderViewWidget(int logicalIndex, QWidget *widget)
{
    headerViewWidgets.insert(logicalIndex, widget);
    if (widget != nullptr)
        widget->setParent(this);
}

void CustomizeHeaderView::setSectionHidden(int logicalIndex, bool hide)
{
    // 先调用父类函数实现默认功能
    QHeaderView::setSectionHidden(logicalIndex, hide);
    // 如果该索引设置了控件,则同步隐藏/显示控件
    if (headerViewWidgets.contains(logicalIndex) &&
            headerViewWidgets.value(logicalIndex) != nullptr)
    {
        headerViewWidgets.value(logicalIndex)->setHidden(hide);
    }
}

// 该函数在QHeaderView类中是const成员函数,派生类重写该虚函数时要保留const,否则基类不会调用
void CustomizeHeaderView::paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const
{
    QHeaderView::paintSection(painter, rect, logicalIndex);

    if (headerViewWidgets.contains(logicalIndex) &&
            headerViewWidgets.value(logicalIndex) != nullptr)
    {
        headerViewWidgets.value(logicalIndex)->setGeometry(rect);
        headerViewWidgets.value(logicalIndex)->setVisible(true);
    }
}

3.2 自定义MyTableDelegate委托类

该类有一个枚举用于标识编号以及批量选中,并通过重写paint()函数绘制编号以及全选项。

enum class DelegateColumnIndex
{
	NumberColumn,       // 编号
	BatchCheckColumn,   // 批量选中
};

mytabledelegate.cpp源文件

MyTableDelegate::MyTableDelegate(QObject *parent)
    : QStyledItemDelegate{parent}
{
    checkedPix.load(":/icons/checked.png");
    checkedPix = checkedPix.scaled(QSize(26, 26), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
    uncheckedPix.load(":/icons/unchecked.png");
    uncheckedPix = uncheckedPix.scaled(QSize(26, 26), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);

}

void MyTableDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    QStyledItemDelegate::paint(painter, option, index);

    if (index.column() == int(DelegateColumnIndex::NumberColumn)) {
        qApp->style()->drawItemText(painter, option.rect, Qt::AlignCenter, QApplication::palette(), true, QString("%1").arg(index.row() + 1));
    } else if (index.column() == int(DelegateColumnIndex::BatchCheckColumn)) {
        if (index.data(Qt::UserRole).toBool()) {
            qApp->style()->drawItemPixmap(painter, option.rect, Qt::AlignCenter, checkedPix);
        } else {
            qApp->style()->drawItemPixmap(painter, option.rect, Qt::AlignCenter, uncheckedPix);
        }
    }
}

3.3 MyTableWidget类

该类将搭配MyTableDelegate委托类和CustomizeHeaderView类一起,主要实现对数据项的一些设置和获取的操作,还有对表格控件的一些初始化操作。
mytablewidget.cpp源文件

void MyTableWidget::setItemText(int row, int column, const QString &text, Qt::Alignment alignment)
{
    QTableWidgetItem *item = this->item(row, column);
    if (item) {
        item->setText(text);
        item->setTextAlignment(alignment);
    } else {
        item = new QTableWidgetItem(text);
        item->setTextAlignment(alignment);
        this->setItem(row, column, item);
    }
}

// 设置item指定角色对应的数据
void MyTableWidget::setItemData(int row, int column, QVariant variant, Qt::ItemDataRole role)
{
    QTableWidgetItem *item = this->item(row, column);
    if (item) {
        item->setData(role, variant);
    } else {
        item = new QTableWidgetItem;
        item->setData(role, variant);
        this->setItem(row, column, item);
    }
}

void MyTableWidget::initTable()
{
    this->setFocusPolicy(Qt::NoFocus); // 无焦点
    this->setShowGrid(false); // 不显示网格线
    this->verticalHeader()->setVisible(false);   // 隐藏垂直表头
    this->horizontalHeader()->setVisible(false);
    this->setEditTriggers(QAbstractItemView::NoEditTriggers); // 表格不可编辑
    this->setSelectionMode(QAbstractItemView::ExtendedSelection); // 设置选中的模式
    this->setSelectionBehavior(QAbstractItemView::SelectRows);  // 设置选中的行为 整行选中
}

3.4 主界面

  1. 在主界面初始化自定义表格控件一些属性
void Widget::initTableWidget()
{
    ui->tableWidget->setRowCount(12);
    ui->tableWidget->setColumnCount(6);

    m_tableDelegate = new MyTableDelegate(this);
    ui->tableWidget->setItemDelegate(m_tableDelegate);
    m_headerView = new CustomizeHeaderView(Qt::Horizontal, this);
    ui->tableWidget->setHorizontalHeader(m_headerView);

    QList<int> columnWidths;
    columnWidths << 50 << 30 << 300 << 170 << 100 << 120;
    for (int i = 0; i < 6; ++i) {
        ui->tableWidget->setColumnWidth(i, columnWidths.at(i));
    }

    ui->tableWidget->verticalHeader()->setDefaultSectionSize(50); // 设置行高
    connect(ui->tableWidget, &MyTableWidget::cellClicked, this, &Widget::onTableCellClicked);

    currentSortColumn = 4;
    currentSortOrder = Qt::DescendingOrder;

    m_headerView->setVisible(true);
    m_headerView->setSortIndicatorShown(true); // 显示排序指示符
    m_headerView->setSortIndicator(currentSortColumn, currentSortOrder);
    m_headerView->setSectionsClickable(true);

    // 水平表头全选按钮
    m_batchCheckBtn = new QToolButton(m_headerView);
    m_batchCheckBtn->setFixedSize(30, 30);
    m_batchCheckBtn->setCheckable(true);
    QIcon icon;
    icon.addFile(":/icons/checked.png", QSize(20, 20), QIcon::Normal, QIcon::On);
    icon.addFile(":/icons/unchecked.png", QSize(20, 20), QIcon::Normal, QIcon::Off);
    m_batchCheckBtn->setIcon(icon);
    m_batchCheckBtn->setIconSize(QSize(28, 28));
    m_batchCheckBtn->setStyleSheet("border-style: none;");
    connect(m_batchCheckBtn, &QToolButton::clicked, this, &Widget::onBatchCheckClicked);

    m_headerView->setHeaderViewWidget(int(MyTableDelegate::DelegateColumnIndex::BatchCheckColumn),
                                      m_batchCheckBtn);
    connect(m_headerView, &CustomizeHeaderView::sectionClicked, this, &Widget::onHeaderSectionClicked);

    // 设置表头
    QStringList horizontaHeaderLables;
    horizontaHeaderLables.append("编号");
    horizontaHeaderLables.append("");
    horizontaHeaderLables.append("文件名");
    horizontaHeaderLables.append("修改时间");
    horizontaHeaderLables.append("类型");
    horizontaHeaderLables.append("大小");
    ui->tableWidget->setHorizontalHeaderLabels(horizontaHeaderLables);
}
  1. 准备数据文件
void Widget::initTableData()
{
    QFile file(":/data.txt");
    if (!file.open(QFile::ReadOnly)) {
        qInfo() << "文件打开失败!";
        return;
    }
    QString line;
    QStringList rowList;
    while (!file.atEnd()) {
        line = file.readLine();
        rowList.append(line);
    }
    initContentsOfTableRow(rowList);
    file.close();
}
  1. 以及表格单元格项点击处理函数、批量选中工具按钮处理还有水平表头section点击处理函数。
void Widget::onTableCellClicked(int row, int column)
{
    if (column == int(MyTableDelegate::DelegateColumnIndex::BatchCheckColumn)) {
        bool checkState = ui->tableWidget->itemData(row, column).toBool();
        ui->tableWidget->setItemData(row, column, !checkState);
    }
}

void Widget::onBatchCheckClicked(bool checked)
{
    int rowCount = ui->tableWidget->rowCount();
    for (int i = 0; i < rowCount; ++i) {
        ui->tableWidget->setItemData(i, 1, checked);
    }
}

void Widget::onHeaderSectionClicked(int logicalIndex)
{
    if (logicalIndex < 2) {
        m_headerView->setSortIndicator(currentSortColumn, currentSortOrder);
        return;
    }
    // 返回具有排序指示符的部分的逻辑索引
    currentSortColumn = m_headerView->sortIndicatorSection();
    // 返回排序指示符的顺序
    currentSortOrder = m_headerView->sortIndicatorOrder();

    sortTableContents();
}

4 总结

对于QTableWidget类的常用点

  1. 基本使用(创建其对象并设置行列数、表格单元格相关操作、设置表格头部标题)
  2. 数据操作(增删改查)
  3. 样式设置
  4. 信号与槽(单元格发生变化信号、表格项被双击信号、表格项被点击信号…)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值