zoukankan      html  css  js  c++  java
  • [Qt]自定义表头实现过滤功能

    1. 写在前面

       过滤功能源自项目上交互优化用户体验,在表头添加过滤符号实现过滤,替换以往在表格上方占用一行过滤项进行过滤。

    2. 过滤提示

      过滤提示就是三态图标(normal,hover,press)。这三种状态的实现通过鼠标移动事件和鼠标点击事件来实现。具体实现如下:

      1)hover状态在鼠标移动事件中实现

    void CFilterHeaderView::mouseMoveEvent(QMouseEvent *e)
    {
        m_hover = logicalIndexAt(e->pos());
        if (m_hover != -1)
            updateSection(m_hover);
        QHeaderView::mouseMoveEvent(e);
    }
    
    bool CFilterHeaderView::event(QEvent *e)
    {
        switch(e->type())
        {
        case QEvent::Leave:
        case QEvent::HoverLeave:
            if (m_hover != -1)
                updateSection(m_hover);
            m_hover = -1;
            break;
        default:
            break;
        }
        return QHeaderView::event(e);
    }

    如果悬浮在某一列上,hover值等于该列的index,否则等于-1。如果hover值不等于-1,则刷新该列(updateSection)。

    mouseMoveEvent中检测鼠标悬浮在那个表格列上。event函数中监听Leave和HoverLeave事件。

       2)press状态在鼠标点击事件中实现

    void CFilterHeaderView::mousePressEvent(QMouseEvent *e)
    {
        m_press = logicalIndexAt(e->pos());
        if (m_press != -1)
            updateSection(m_press);
        QHeaderView::mousePressEvent(e);
    }
    
    void CFilterHeaderView::mouseReleaseEvent(QMouseEvent *e)
    {
        m_press = -1;
        QHeaderView::mouseReleaseEvent(e);
    }

    press的实现较为简单,鼠标点击更新press,鼠标释放press置为-1。

      3)过滤提示的实现。

      过滤提示在paintSection函数中实现,首先是调用基类paintSection实现表头的绘制,然后是检测有没有定义过滤角色。如果有定义过滤角色,则根据三态选择对应的图标,绘制位置默认水平靠右垂直居住,也可以自己指定位置。最后是绘制过滤提示。具体实现如下:

    void CFilterHeaderView::paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const
    {
        painter->save();
        QHeaderView::paintSection(painter, rect, logicalIndex);
        painter->restore();
    
        QVariant filterVar = model()->headerData(logicalIndex, orientation(), FilterRole);
        if (filterVar.isValid() && filterVar.toBool())
        {
            QPixmap pix = m_norFilterPix;
            bool b_contain = getFilterRect(rect).contains(cursor().pos());
            if (logicalIndex == m_hover && b_contain)
            {
                pix = m_hovFilterPix;
            }
            if (logicalIndex == m_press && b_contain)
            {
                pix = m_preFilterPix;
            }
    
            int align = Qt::AlignRight | Qt::AlignVCenter;
            QVariant alignVar = model()->headerData(logicalIndex, orientation(), FilterAlignmentRole);
            if (alignVar.isValid())
            {
                align = alignVar.toInt();
            }
            style()->drawItemPixmap(painter, rect, align, pix);
        }
    }

    表格绘制的区域和过滤提示绘制的区域不一致,要根据过滤图标大小进行计算过滤提示的区域。只有当鼠标在过滤区域位置上方,hover和press才有效,否则仍然是normal状态。过滤区域绘制的位置可以从外面获取,也可以使用默认位置。最后style()->drawItemPixmap进行绘制。

    调用基类paintSection方法前后调用QPainter::save()和QPainter::restore()是必要的。如果不调用,style()->drawItemPixmap是不会起作用的。

    4)过滤提示点击信号

    点击过滤提示会发出信号,连接此信号可以进行过滤功能的实现。具体实现如下:

    void CFilterHeaderView::mouseReleaseEvent(QMouseEvent *e)
    {
        if (e->button() == Qt::LeftButton)
        {
            int section = logicalIndexAt(e->pos());
            QVariant filterVar = model()->headerData(section, orientation(), FilterRole);
            if (filterVar.isValid() && filterVar.toBool())
            {
                QRect rect(sectionViewportPosition(section), 0, sectionSize(section), height());
                if (getFilterRect(rect).contains(cursor().pos()))
                {
                    emit filterClicked(section);
                }
            }
        }
        QHeaderView::mouseReleaseEvent(e);
    }

    过滤信号发出的条件:1. 左键点击,2. 定义了过滤功能,3. 鼠标在过滤提示区域中

    3. 使用过滤功能

    使用过滤表头的方法如下:

        m_tableView = new QTableView(this);
        m_model = new QStandardItemModel(this);
        m_filterModel = new QSortFilterProxyModel(this);
        m_filterModel->setSourceModel(m_model);
        m_filterModel->setSortRole(Qt::ToolTipRole);
        m_tableView->setModel(m_filterModel);
    
        QHBoxLayout* mainLayout = new QHBoxLayout(this);
        mainLayout->setMargin(0);
        mainLayout->addWidget(m_tableView);
        setLayout(mainLayout);
    
        m_tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
        m_tableView->setSelectionMode(QAbstractItemView::SingleSelection);
        m_tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
    
        m_tableView->verticalHeader()->hide();
        CFilterHeaderView* pHeader = new CFilterHeaderView(this);
        connect(pHeader, &CFilterHeaderView::filterClicked, this, &Widget::onFilterClicked);
        m_tableView->setHorizontalHeader(pHeader);

    使用过滤表头和使用普通表头没有太大的差别。这里过滤功能有QSortFilterProxyModel实现,水平表头替换成自定义的CFilterHeaderView。

    4. 写在最后

    实现过滤表头的原理如上所诉。具体完整的项目路径参考如下地址:

    https://github.com/zhugp125/QtDemo/tree/master/FilterHeaderView

  • 相关阅读:
    python基础--二分查找
    python基础--字典
    python基础--列表和元组
    python基础--基本数据类型的概述
    python基础--循环
    python基础--变量和基础数据类型
    Python2与Python3区别
    project euler之最大的回文产品
    project euler之最大的素因子
    project euler之甚至斐波那契数字(Even Fibonacci numbers)
  • 原文地址:https://www.cnblogs.com/zhugaopeng/p/11146809.html
Copyright © 2011-2022 走看看