头文件 frozencolumnheader.h

cpp

#ifndef FROZENCOLUMNHEADER_H
#define FROZENCOLUMNHEADER_H

#include <QHeaderView>
#include <QTableView>
#include <QPainter>
#include <QMouseEvent>
#include <QStyleOptionHeader>
#include <QItemSelectionModel>
#include <QEvent>
#include <QToolTip>

class FrozenColumnHeader : public QHeaderView
{
    Q_OBJECT

public:
    explicit FrozenColumnHeader(QTableView* tableView);
    ~FrozenColumnHeader() override = default;

    // 新增方法:设置字体
    void setHeaderFont(const QFont& font);
    void setHeaderFontSize(int pointSize);
    void setHeaderFontBold(bool bold);
    void setHeaderTextColor(const QColor& color);

    // 新增方法:设置工具提示相关属性
    void setToolTipEnabled(bool enabled);
    bool isToolTipEnabled() const;
    void setToolTipDuration(int milliseconds);
    int toolTipDuration() const;

signals:
    void sectionClicked(int logicalIndex);

protected:
    void paintSection(QPainter* painter, const QRect& rect, int logicalIndex) const override;
    void mousePressEvent(QMouseEvent* event) override;
    void mouseMoveEvent(QMouseEvent* event) override;
    void leaveEvent(QEvent* event) override;
    QSize sizeHint() const override;
    bool event(QEvent* event) override;

private slots:
    void onSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected);

private:
    QString getElidedText(const QString& text, const QFont& font, int width) const;
    bool needToolTip(const QString& text, const QFont& font, int width) const;

private:
    QTableView* m_tableView;
    int m_hoveredSection = -1;
    QFont m_headerFont;          // 新增:字体设置
    QColor m_headerTextColor;    // 新增:文字颜色
    bool m_toolTipEnabled = true; // 新增:是否启用工具提示
    int m_toolTipDuration = 3000; // 新增:工具提示显示时间(毫秒)
    QString m_lastToolTipText;   // 新增:上次显示的工具提示文本
};

#endif // FROZENCOLUMNHEADER_H

实现文件 frozencolumnheader.cpp

cpp

#include "frozencolumnheader.h"
#include <QDebug>
#include <QFontMetrics>

FrozenColumnHeader::FrozenColumnHeader(QTableView* tableView)
    : QHeaderView(Qt::Vertical, tableView), m_tableView(tableView)
{
    // 初始化默认字体设置
    m_headerFont = QFont("Microsoft YaHei"); // 默认字体和大小
    m_headerFont.setPixelSize(12);
    m_headerTextColor = Qt::black;               // 默认文字颜色

    setSectionsClickable(true);
    setHighlightSections(true);
    //setDefaultSectionSize(35);
    setMinimumWidth(200);
    setAttribute(Qt::WA_Hover, true);

    // 连接选择变化信号
    if (tableView && tableView->selectionModel()) {
        connect(tableView->selectionModel(), &QItemSelectionModel::selectionChanged,
            this, &FrozenColumnHeader::onSelectionChanged);
    }
}

// 设置字体
void FrozenColumnHeader::setHeaderFont(const QFont& font)
{
    m_headerFont = font;
    update(); // 重绘
}

// 设置字体大小
void FrozenColumnHeader::setHeaderFontSize(int pointSize)
{
    m_headerFont.setPointSize(pointSize);
    update(); // 重绘
}

// 设置字体加粗
void FrozenColumnHeader::setHeaderFontBold(bool bold)
{
    m_headerFont.setBold(bold);
    update(); // 重绘
}

// 设置文字颜色
void FrozenColumnHeader::setHeaderTextColor(const QColor& color)
{
    m_headerTextColor = color;
    update(); // 重绘
}

// 设置是否启用工具提示
void FrozenColumnHeader::setToolTipEnabled(bool enabled)
{
    m_toolTipEnabled = enabled;
}

// 获取工具提示启用状态
bool FrozenColumnHeader::isToolTipEnabled() const
{
    return m_toolTipEnabled;
}

// 设置工具提示显示时间
void FrozenColumnHeader::setToolTipDuration(int milliseconds)
{
    m_toolTipDuration = milliseconds;
}

// 获取工具提示显示时间
int FrozenColumnHeader::toolTipDuration() const
{
    return m_toolTipDuration;
}

// 获取省略文本
QString FrozenColumnHeader::getElidedText(const QString& text, const QFont& font, int width) const
{
    QFontMetrics metrics(font);
    return metrics.elidedText(text, Qt::ElideRight, width - 10); // 留出10像素的边距
}

// 检查是否需要显示工具提示
bool FrozenColumnHeader::needToolTip(const QString& text, const QFont& font, int width) const
{
    if (text.isEmpty()) {
        return false;
    }
    
    QFontMetrics metrics(font);
    return metrics.horizontalAdvance(text) > (width - 10); // 留出10像素的边距
}

void FrozenColumnHeader::paintSection(QPainter* painter, const QRect& rect, int logicalIndex) const
{
    if (!m_tableView || !m_tableView->model())
        return;

    if (!painter || logicalIndex < 0 || logicalIndex >= count()) {
        return;
    }

    // 使用静态变量缓存颜色
    static const QColor selectedBgColor("#0078d7");
    static const QColor selectedBorderColor("#005a9e");
    static const QColor normalBgColor("#f8f8f8");
    static const QColor normalBorderColor("#d0d0d0");
    static const QColor hoverBgColor("#e3f2fd");
    static const QColor textColorWhite(Qt::white);
    static const QColor textColorBlack(Qt::black);

    // 关键修复:正确检查选择状态
    bool isSelected = false;
    if (m_tableView->selectionModel()) {
        // 检查该行是否有任何选中的单元格
        QModelIndex index = m_tableView->model()->index(logicalIndex, 0);
        isSelected = m_tableView->selectionModel()->isSelected(index);
    }

    const bool isHovered = (logicalIndex == m_hoveredSection);

    // 获取数据
    const QString data = m_tableView->model()->data(
        m_tableView->model()->index(logicalIndex, 0)).toString();

    painter->save();

    // 确定背景和文字颜色
    QColor backgroundColor;
    QColor textColor;
    QColor borderColor;

    if (isSelected) {
        backgroundColor = selectedBgColor;
        textColor = textColorWhite;
        borderColor = selectedBorderColor;
    }
    else if (isHovered) {
        backgroundColor = hoverBgColor;
        textColor = textColorBlack;
        borderColor = normalBorderColor;
    }
    else {
        backgroundColor = normalBgColor;
        textColor = textColorBlack;
        borderColor = normalBorderColor;
    }

    // 绘制背景
    painter->fillRect(rect, backgroundColor);

    // 绘制边框
    painter->setPen(palette().mid().color());
    painter->drawRect(rect);

    // 设置字体和文字颜色
    painter->setFont(m_headerFont);
    painter->setPen(m_headerTextColor);

    // 绘制文本
    painter->setPen(textColor);
    QFont font = painter->font();
    if (isSelected) {
        font.setBold(true);
    }
    painter->setFont(font);
    
    // 如果文本太长,显示省略号
    QString displayText = getElidedText(data, font, rect.width());
    painter->drawText(rect, Qt::AlignCenter, displayText);

    // 绘制边框
    painter->setPen(borderColor);
    painter->drawRect(rect.adjusted(0, 0, -1, -1));

    painter->restore();
}

bool FrozenColumnHeader::event(QEvent* event)
{
    if (m_toolTipEnabled && event->type() == QEvent::ToolTip) {
        QHelpEvent* helpEvent = static_cast<QHelpEvent*>(event);
        int logicalIndex = logicalIndexAt(helpEvent->pos());
        
        if (logicalIndex >= 0 && logicalIndex < count()) {
            // 获取数据
            QString data = m_tableView->model()->data(
                m_tableView->model()->index(logicalIndex, 0)).toString();
            
            // 获取该section的矩形区域
            QRect sectionRect(sectionViewportPosition(logicalIndex), 0, 
                             sectionSize(logicalIndex), height());
            
            // 检查是否需要显示工具提示
            if (needToolTip(data, m_headerFont, sectionRect.width())) {
                QToolTip::showText(helpEvent->globalPos(), data, this, 
                                  sectionRect, m_toolTipDuration);
                m_lastToolTipText = data;
                return true;
            }
        }
        
        QToolTip::hideText();
        event->ignore();
        return true;
    }
    
    return QHeaderView::event(event);
}

void FrozenColumnHeader::mousePressEvent(QMouseEvent* event)
{
    if (!m_tableView || !m_tableView->selectionModel()) {
        QHeaderView::mousePressEvent(event);
        return;
    }

    const int logicalIndex = logicalIndexAt(event->pos());
    if (logicalIndex >= 0) {
        // 清除所有现有选择
        m_tableView->selectionModel()->clearSelection();

        // 选择整行(包括隐藏的第一列)
        for (int col = 0; col < m_tableView->model()->columnCount(); ++col) {
            QModelIndex index = m_tableView->model()->index(logicalIndex, col);
            m_tableView->selectionModel()->select(index, QItemSelectionModel::Select);
        }

        emit sectionClicked(logicalIndex);
    }
    else {
        // 点击表头空白处时清除所有选择
        m_tableView->selectionModel()->clearSelection();
    }

    update();
    QHeaderView::mousePressEvent(event);
}

void FrozenColumnHeader::mouseMoveEvent(QMouseEvent* event)
{
    QHeaderView::mouseMoveEvent(event);

    const int newHoveredSection = logicalIndexAt(event->pos());
    if (newHoveredSection != m_hoveredSection) {
        m_hoveredSection = newHoveredSection;
        update();
    }
}

void FrozenColumnHeader::leaveEvent(QEvent* event)
{
    QHeaderView::leaveEvent(event);
    m_hoveredSection = -1;
    update();
}

QSize FrozenColumnHeader::sizeHint() const
{
    return QSize(200, QHeaderView::sizeHint().height());
}

void FrozenColumnHeader::onSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected)
{
    Q_UNUSED(selected);
    Q_UNUSED(deselected);

    // 强制立即更新整个表头
    update();
    // 确保视图也更新
    if (viewport()) {
        viewport()->update();
    }
}

主要改进内容:

  1. 添加工具提示功能

    • 重写 event() 方法处理 QEvent::ToolTip 事件

    • 添加 setToolTipEnabled()isToolTipEnabled() 等方法控制工具提示行为

    • 添加 setToolTipDuration()toolTipDuration() 方法设置工具提示显示时间

  2. 文本省略处理

    • 添加 getElidedText() 方法处理文本过长时的省略显示

    • 添加 needToolTip() 方法判断是否需要显示工具提示

    • 在 paintSection() 中使用省略文本绘制

  3. 智能工具提示显示

    • 只在文本确实过长时才显示工具提示

    • 工具提示显示完整内容

    • 工具提示位置与对应的表头section对齐

  4. 性能优化

    • 只在需要时显示工具提示,避免不必要的提示

    • 使用字体度量准确计算文本宽度

使用方法:

cpp

FrozenColumnHeader* header = new FrozenColumnHeader(tableView);
header->setToolTipEnabled(true); // 启用工具提示(默认已启用)
header->setToolTipDuration(5000); // 设置工具提示显示5秒

这样改进后,当表头内容太长显示不全时,鼠标悬停会显示完整的文本内容,提升了用户体验。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐