1. 程式人生 > >[Qt]自定義表頭實現過濾功能

[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