QTableView表格拖拽左右滑动条第一列不动, 实现冻结列效果,筛选表格功能效果。

效果图

在这里插入图片描述

方案一

  • 参考QT示例项目中代码,简单思路为使用俩个QtaleView A,B进行处理,A表格覆盖在B表格之上,A表格只显示第一行,其余行进行隐藏处理。关键在于俩个表格要使用同一份模型,和光标同步。
    // 设置和父table同步的光标选择单元格(必要),修改统一数据
    frozenTableView->setSelectionModel(selectionModel());
    // 将tableview放到frozenTableView视图下,进行覆盖
    viewport()->stackUnder(frozenTableView);

在表格进行变化时,通过检测resizeEvent函数来进行处理。

  • .h文件
class MyTableView : public QTableView
{
    Q_OBJECT
public:
    MyTableView(QLabel *label, QSortFilterProxyModel *sortModel, QWidget *parent = 0);
    ~MyTableView() { delete frozenTableView; }

protected:
    /// @brief 在QTableView的大小调整时进行自定义的处理操作,更新表格的内容,调整表格的列宽和行高
    /// @param event
    void resizeEvent(QResizeEvent *event) override;
    /// @brief 可以自定义滚动的行为
    /// @param index 表示需要滚动到可见区域的模型索引。
    /// @param hint 表示滚动的提示,有多个选项可用,其中EnsureVisible是默认选项,表示确保索引可见。
    void scrollTo(const QModelIndex &index, ScrollHint hint = EnsureVisible) override;

protected:
    QLabel *messageLabel;

private:
    QTableView *frozenTableView;
    void init();
    /// 变换表格长度
    void updateFrozenTableGeometry();

private slots:
    void updateSectionWidth(int logicalIndex, int oldSize, int newSize);
     /// @brief 拖拽表头时,实时变换长度
    /// @param logicalIndex 
    /// @param oldSize 
    /// @param newSize 
    void updateSectionHeight(int logicalIndex, int oldSize, int newSize);
};
  • .cpp文件
MyTableView::MyTableView(QLabel *label, QSortFilterProxyModel *sortModel, QWidget *parent)
    : QTableView(parent), messageLabel(label)
{
    frozenTableView = new QTableView(this);
    // 设置代理模型
    setModel(sortModel);
    frozenTableView->setModel(sortModel);
    init();

    connect(horizontalHeader(), &QHeaderView::sectionResized, this, &MyTableView::updateSectionWidth);
    connect(verticalHeader(), &QHeaderView::sectionResized, this, &MyTableView::updateSectionHeight);

    connect(frozenTableView->verticalScrollBar(), &QAbstractSlider::valueChanged, verticalScrollBar(),
            &QAbstractSlider::setValue);
    connect(verticalScrollBar(), &QAbstractSlider::valueChanged, frozenTableView->verticalScrollBar(),
            &QAbstractSlider::setValue);
};

void MyTableView::resizeEvent(QResizeEvent *event)
{
    QTableView::resizeEvent(event);
    updateFrozenTableGeometry();
}

void MyTableView::scrollTo(const QModelIndex &index, ScrollHint hint)
{
    if (index.column() > 0)
        QTableView::scrollTo(index, hint);
}

void MyTableView::init()
{
    //**TableView**//
    // 升序
    sortByColumn(ElecWizardTableModel::ParamClassify, Qt::AscendingOrder);
    // 设置委托
    setItemDelegate(new ElecComboxDelegate(this));
    // 水平表头允许拖拽调整宽度
    horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive);
    // 垂直表头不可见
    verticalHeader()->setVisible(false);
    // 点击表头允许排序
    setSortingEnabled(true);
    // 隐藏排序后出现的箭头
    horizontalHeader()->setSortIndicatorShown(false);
    // 任意键都可以来编辑表格中的单元格
    setEditTriggers(QTableView::AllEditTriggers);
    // 将tableview放到frozenTableView视图下,进行覆盖
    viewport()->stackUnder(frozenTableView);
    // // 最后一列自动填充列宽
    // horizontalHeader()->setStretchLastSection(true);
    // // 以像素为单位滚动
    // setHorizontalScrollMode(ScrollPerPixel);
    // setVerticalScrollMode(ScrollPerPixel);
    // end

    //**frozenTableView**//
    // 升序
    frozenTableView->sortByColumn(ElecWizardTableModel::ParamClassify, Qt::AscendingOrder);
    // 设置无焦点
    frozenTableView->setFocusPolicy(Qt::NoFocus);
    // 点击表头允许排序
    frozenTableView->setSortingEnabled(true);
    // 隐藏排序后出现的箭头
    frozenTableView->horizontalHeader()->setSortIndicatorShown(false);
    // 隐藏垂直表头
    frozenTableView->verticalHeader()->hide();
    // frozenTableView水平表头的各个段宽度无法被拖动调整大小
    frozenTableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed);
    // 设置和父table同步的光标选择单元格(必要),修改统一数据
    frozenTableView->setSelectionModel(selectionModel());
    // 隐藏frozenTableView第一列以外的数据
    for (int col = 1; col < ElecWizardTableModel::Column::end; ++col)
        frozenTableView->setColumnHidden(col, true);
    // frozenTableView使用与tableview第一列相同的宽高
    frozenTableView->setColumnWidth(0, columnWidth(0));
    frozenTableView->setRowHeight(0, rowHeight(0));
    // frozenTableView不显示滑动条
    frozenTableView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    frozenTableView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    frozenTableView->show();
    // // 以像素为单位滚动
    // frozenTableView->setVerticalScrollMode(ScrollPerPixel);
    // end

    updateFrozenTableGeometry();
}

void MyTableView::updateFrozenTableGeometry()
{
    // 获取单个单元格的高度
    QModelIndex index = model()->index(0, 0);
    QRect rect = visualRect(index);
    // 行数*行高+表头高+行数(边线1px)
    int height = model()->rowCount() * rect.height() + horizontalHeader()->height() + model()->rowCount();
    // 当前视图下的高度
    int height2 = viewport()->height() + horizontalHeader()->height();
    // 当表格未满时使用height,防止第一列出现多余的框
    int resultHeight = height2 < height ? height2 : height;
    // 设置第一列的几何大小
    frozenTableView->setGeometry(verticalHeader()->width() + frameWidth(), frameWidth() - 1, columnWidth(0),
                                 resultHeight);
}

void MyTableView::updateSectionHeight(int logicalIndex, int oldSize, int newSize)
{
    frozenTableView->setRowHeight(logicalIndex, newSize);
}

void MyTableView::updateSectionWidth(int logicalIndex, int oldSize, int newSize)
{
    if (logicalIndex == 0) {
        frozenTableView->setColumnWidth(0, newSize);
        updateFrozenTableGeometry();
    }
}

方案二

  • 可以通过自定义QScrollBar滑动条,加入到布局中,隐藏原有表格的滑动条,通过自定义的滑动条与表格的联动算法从而实现冻结列的效果。

  • 自定义滑动条代码如下

class CustomHorizontalScrollBar : public QScrollBar
{
    Q_OBJECT

public:
    explicit CustomHorizontalScrollBar(QWidget* parent = nullptr);
    ~CustomHorizontalScrollBar();
    void initCustomScrollbar(const int& freezecols);
    int getFreezeColno() const { return m_freezeCols; }

private slots:
    void onValueChanged(int value);

signals:
    void scrollbarValueChanged(bool bscrollright, const int& begincol, const int& changecolno);

private:
    int m_freezeCols;
    int m_oldposition;
};

// 自定义滚动条
CustomHorizontalScrollBar::CustomHorizontalScrollBar(QWidget* parent /*= nullptr*/)
    : QScrollBar(parent), m_freezeCols(0), m_oldposition(0)
{
    this->setOrientation(Qt::Horizontal);
    this->setSingleStep(10);
    connect(this, SIGNAL(valueChanged(int)), this, SLOT(onValueChanged(int)));
}

void CustomHorizontalScrollBar::initCustomScrollbar(const int& freezecols)
{
    m_freezeCols = freezecols;
    m_oldposition = 0;
    this->setValue(0);
}

void CustomHorizontalScrollBar::onValueChanged(int value)
{
    // 左滑
    if (m_oldposition > value) {
        emit scrollbarValueChanged(false, m_freezeCols + m_oldposition - 1, m_oldposition - value);
    } else if (m_oldposition < value) {
        // 右滑
        emit scrollbarValueChanged(true, m_freezeCols + m_oldposition, value - m_oldposition);
    }
    m_oldposition = value;
}

自定义滑动条中要维护一个固定列数和记录上一次位置的变量,通过滑动条的valueChanged信号触发自定义信号onValueChanged信号,从而连接到widget上表格的槽函数onHorizontalValueChanged,最终通过其联动处理实现固定行滑动的效果。

//与自定义的信号连接
connect(mCustomHorizontalScrollBar, &CustomHorizontalScrollBar::scrollbarValueChanged, this,
                         &myWidget::onHorizontalValueChanged);

/**
 * @brief myWidget::onHorizontalValueChanged
 * 水平滚动条值改变时的槽函数
 * @param bscrollright 是否向右滚动
 * @param begincol 开始显示的列索引
 * @param changecolsno 修改的列数
 */
void myWidget::onHorizontalValueChanged(bool bscrollright, const int& begincol, const int& changecolsno)
{
    // 检查是否正在更新宽度或滚动
    if (m_isUpdateWidthOrScroll) {
        return;
    }
    QList<int> visiblecolumnsNo; // 表头显示的列;0表示第一列,与表格保持一致
    // QList<TableSetItem> tablelist = tableSetPropertyMap[currentTableName];
    // // 遍历所有的表头属性,找出需要显示的列
    // for (int i = 0; i < tablelist.size(); ++i) {
    //     if (tablelist[i].bDisplay) {
    //         visiblecolumnsNo.append(i);
    //     }
    // }
    int candisplaycolumns = visiblecolumnsNo.size(); // 表格表头的属性【显示】的列数
    // 如果没有要显示的列或者开始列的索引不合法,直接返回
    if (candisplaycolumns <= 0 || begincol < 0) {
        return;
    }
    if (bscrollright) {
        // 如果开始列加上要修改的列数超过了可以显示的列数,直接返回
        if (candisplaycolumns <= begincol + changecolsno - 1) {
            return;
        }
        // 隐藏指定的列
        for (int i = 0; i < changecolsno; ++i) {
            ui.tableView->hideColumn(visiblecolumnsNo[begincol + i]);
        }
    } else {
        // 如果开始列小于等于要修改的列数的索引或者开始列的索引不符合要求,直接返回
        if (candisplaycolumns <= begincol || begincol < changecolsno - 1) {
            return;
        }
        // 显示指定的列
        for (int i = 0; i < changecolsno; ++i) {
            ui.tableView->showColumn(visiblecolumnsNo[begincol - i]);
        }
    }
}

筛选表格功能

在这里插入图片描述

  • QSortFilterProxyModel是Qt框架中的一个代理模型类,用于对其他模型数据进行排序和过滤。
class CustomProxyModel : public QSortFilterProxyModel
{
public:
    CustomProxyModel(QObject *parent = nullptr)
        : QSortFilterProxyModel(parent),
          m_strFilterString(""),
          m_FilterColumnStr(""),
          m_FilterCol(0)
    {
    }

    void setFilterString(const QString &strFilter)
    {
        m_strFilterString = strFilter;
        invalidateFilter();
    }

    void setFilterColumn(const QString &colname, const int &colno)
    {
        m_FilterColumnStr = colname;
        m_FilterCol = colno;
        invalidateFilter();
    }

public:
    /// @brief 对源模型进行排序和过滤操作
    /// @param source_row 源模型中的行号
    /// @param source_parent 源模型中行的父索引。
    /// @return
    virtual bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override
    {
        QModelIndex index = sourceModel()->index(source_row, 0, source_parent);

        QString strValue = index.sibling(index.row(), m_FilterCol).data().toString();
        bool falg = strValue.contains(m_strFilterString, Qt::CaseInsensitive);
        return falg;
    }

private:
    QString m_strFilterString; // 输入框文本
    QString m_FilterColumnStr; // 下拉框文本
    int m_FilterCol;           // 下拉框序号
};
  • setFilterColumnsetFilterString俩个函数分别是下拉框与文本框调用的函数,当其被调用时,触发函数中的invalidateFilter函数,就是调用filterAcceptsRow进行筛选。
  • 7
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 19
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 19
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值