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

  • 相关阅读:
    消息中间件(一)MQ详解及四大MQ比较
    SIP协议
    PAT (Basic Level) Practice 1008 数组元素循环右移问题
    LeetCode-Algorithms 1. 两数之和
    PAT (Basic Level) Practice 1040 有几个PAT
    PAT (Basic Level) Practice 1023 组个最小数
    PAT (Basic Level) Practice 1021 个位数统计
    PAT (Basic Level) Practice 1007 素数对猜想
    PAT (Basic Level) Practice 1006 换个格式输出整数
    PAT (Basic Level) Practice 1004 成绩排名
  • 原文地址:https://www.cnblogs.com/zhugaopeng/p/11146809.html
Copyright © 2011-2022 走看看