您尚未登录。

楼主 # 2024-09-16 20:47:30

memory
会员
注册时间: 2021-08-11
已发帖子: 487
积分: 464

Qt5 QListView 动态加载数据

main.cpp

#include "virtual_list.h"
#include <QtWidgets/QApplication>
#include <qlistwidget.h>


int main(int argc, char* argv[])
{
	QApplication a(argc, argv);
	VirtualList w;
	QList<QString> list;
	// 插入50W条数据
	for (int i = 0; i < 500000; i++)
	{
        list.push_back(QString("%1").arg(i, 5, 10, QChar('0')));
	}
	w.Append(list);
	w.RefreshData();
	w.show();
	return a.exec();
}

virtual_list.h

#ifndef VIRTUAL_LIST_H
#define VIRTUAL_LIST_H

#include <QtWidgets/QMainWindow>
#include <qlistview.h>
#include <qlist.h>
#include <qstandarditemmodel.h>
#include <qboxlayout.h>
#include <qscrollbar.h>

class VirtualList : public QWidget
{
    Q_OBJECT

public:
    VirtualList(QWidget* parent = nullptr);
    ~VirtualList();

public:
    /**
     * @brief 追加单个数据
     * @param data
    */
    void Append(const QString& data);

    /**
     * @brief 批量追加数据
     * @param dataList
    */
    void Append(const QList<QString>& dataList);

    /**
     * @brief 清空表格
    */
    void Clear();

    /**
     * @brief 获取表格
     * @return
    */
    QListView* GetListView() const;

    /**
     * @brief 重置页面数据
    */
    void RefreshData();

    /**
     * @brief 行数
     * @return
    */
    int RowCount() const;

    /**
     * @brief 列数
     * @return
    */
    int ColumnCount() const;

    /**
     * @brief 获取当前索引
     * @return
    */
    int GetSelectedIndex() const;

    /**
     * @brief 获取指定行的数据
     * @param index 行号,索引从0开始
     * @return
    */
    QString GetData(int index) const;

    /**
     * @brief 获取全部数据
     * @return
    */
    QList<QString> GetAllData() const;

protected:
    void resizeEvent(QResizeEvent* event) override;
    bool eventFilter(QObject* obj, QEvent* event) override;

protected:
    /**
     * @brief 竖向滚动条的指改变
     * @param value
    */
    void OnScrollBarValueChanged(int value);

    /**
     * @brief 处理滚动条(是否显示,以及长度区间)
    */
    void HandleScrollBar();

private:
    /**
     * @brief 计算文本长度
     * @param font
     * @param text
     * @return
    */
    int CalcTextWidth(const QFont& font, const QString& text);

private:
    QListView* m_pListView;
    QScrollBar* m_pScrollBar;
    QList<QString> m_dataList;
    QStandardItemModel* m_pStdModel;
    int m_nShowCount; // 显示的数量
    int m_nCurrentPos; // 滚动条当前位置
    int m_nCurrentIndex; // 当前数据索引
    int m_nSelectedIndex; // 选中的索引
};

#endif

virtual_list.cpp

#include "virtual_list.h"
#include <qscrollbar.h>
#include <qevent.h>
#include <qfontmetrics.h>
#ifdef QT_DEBUG
#include <qdebug.h>
#endif // QT_DEBUG
#include <cmath>

using namespace std;

const static int nStdItemHeight = 40; // item固定40高度

VirtualList::VirtualList(QWidget* parent)
    : QWidget(parent),
    m_nShowCount(0),
    m_nCurrentPos(0),
    m_nCurrentIndex(-1),
    m_nSelectedIndex(-1)
{
    QHBoxLayout* pLayout = new QHBoxLayout(this);
    m_pListView = new QListView(this);
    m_pScrollBar = new QScrollBar(this);
    m_pStdModel = new QStandardItemModel(m_pListView);
    m_pListView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    m_pListView->setResizeMode(QListView::ResizeMode::Adjust);
    m_pListView->setEditTriggers(QListView::NoEditTriggers);
    m_pListView->setModel(m_pStdModel);
    m_dataList.reserve(12000);
    pLayout->addWidget(m_pListView);
    pLayout->addWidget(m_pScrollBar);
    m_pListView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    m_pScrollBar->setSingleStep(nStdItemHeight); // 一次移动一个Item
    m_pScrollBar->setPageStep(nStdItemHeight);
    m_pScrollBar->hide();

    connect(m_pScrollBar, &QScrollBar::valueChanged, this, &VirtualList::OnScrollBarValueChanged);
    connect(m_pListView, &QListView::clicked, this, [&] {
        m_nSelectedIndex = m_nCurrentIndex + m_pListView->currentIndex().row();
        });

    installEventFilter(this);
    m_pListView->installEventFilter(this);
    m_pScrollBar->installEventFilter(this);
}

VirtualList::~VirtualList()
{}

void VirtualList::Append(const QString& data)
{
    m_dataList.push_back(data);
    // 数据追加后,更新索引
    if (m_nCurrentIndex < 0)
    {
        m_nCurrentIndex = 0;
    }
    // 处理下滚动条
    HandleScrollBar();
    // 如果当前数据的数量小于显示数量,那么要刷新一次到界面
    if (m_dataList.size() - 1 < m_nShowCount)
    {
        RefreshData();
    }
}

void VirtualList::Append(const QList<QString>& dataList)
{
    m_dataList.append(dataList);
    // 数据追加后,更新索引
    if (m_nCurrentIndex < 0)
    {
        m_nCurrentIndex = 0;
    }
    // 处理下滚动条
    HandleScrollBar();
    // 如果当前数据的数量小于显示数量,那么要刷新一次到界面
    if (m_dataList.size() - dataList.size() < m_nShowCount)
    {
        RefreshData();
    }
}

void VirtualList::Clear()
{
    m_dataList.clear();
    m_nCurrentPos = 0;
    m_nCurrentIndex = -1;
    m_nSelectedIndex = -1;
    for (int i = m_pStdModel->rowCount() - 1; i >= 0; i--)
    {
        QStandardItem* item = m_pStdModel->item(i, 0);  // 获取QStandardItem对象
        delete item;  // 释放QStandardItem对象
        item = nullptr;
        m_pStdModel->removeRow(i);
    }
}

QListView* VirtualList::GetListView() const
{
    return m_pListView;
}

int VirtualList::ColumnCount() const
{
    return 1;
}

int VirtualList::GetSelectedIndex() const
{
    return m_nSelectedIndex;
}

QString VirtualList::GetData(int index) const
{
    if (index < 0 || index >= m_dataList.size())
    {
        return {};
    }
    return m_dataList[index];
}

QList<QString> VirtualList::GetAllData() const
{
    return m_dataList;
}

void VirtualList::resizeEvent(QResizeEvent* event)
{
    QWidget::resizeEvent(event);

    // 获取重置的窗口大小
    QSize listSize = m_pListView->size();
    m_nShowCount = listSize.height() / nStdItemHeight;
    if (m_nShowCount > 0 && m_dataList.size() > 0)
    {
        HandleScrollBar();

        // 填充数据
        RefreshData();
    }
}

bool VirtualList::eventFilter(QObject* obj, QEvent* event)
{
    if (event->type() == QEvent::Wheel)
    {
        if (obj == m_pScrollBar || obj == m_pListView)
        {
            QWheelEvent* we = static_cast<QWheelEvent*>(event);
            if (we->angleDelta().y() > 0) // 向上滚
            {
                m_pScrollBar->setValue(m_pScrollBar->value() - nStdItemHeight);
            }
            else // 向下滚
            {
                m_pScrollBar->setValue(m_pScrollBar->value() + nStdItemHeight);
            }
            return true;
        }
    }
    return QWidget::eventFilter(obj, event);
}

void VirtualList::OnScrollBarValueChanged(int value)
{
#ifdef QT_DEBUG
    qDebug() << value;
#endif
    // 查看最新的数据索引(最新索引是指即将要展示的首个数据索引)
    int newIndex = std::ceil(value / (nStdItemHeight * 1.0f));

    // 最新的索引超出数组大小
    if (newIndex >= m_dataList.size())
    {
        // 最新的和当前的索引差值小于展示的数量时,直接退出
        if (std::abs(newIndex - m_nCurrentIndex) <= m_nShowCount)
        {
            return;
        }
        else
        {
            --newIndex;
        }
    }
    // 数据索引刷新
    m_nCurrentIndex = newIndex;

    // 取消选中状态
    m_pListView->clearSelection();
    // 看看刚才的选中索引是否在范围内
    if (m_nSelectedIndex >= 0 && m_nSelectedIndex >= newIndex && m_nSelectedIndex <= newIndex + m_nShowCount)
    {
        int indexDiff = m_nSelectedIndex - m_nCurrentIndex;
        m_pListView->setCurrentIndex(m_pStdModel->index(indexDiff, 0));
    }

    // 做下越界防护
    if (m_nCurrentIndex >= m_dataList.size())
    {
        m_nCurrentIndex = m_dataList.size() - 1;
    }
    if (m_nCurrentIndex < 0)
    {
        m_nCurrentIndex = 0;
    }

    // 重置页面数据
    RefreshData();
}

void VirtualList::HandleScrollBar()
{
    if (m_dataList.size() > m_nShowCount)
    {
        // 设置大小,[0, item高度 * 数据长度]
        m_pScrollBar->setRange(0, nStdItemHeight * m_dataList.size());
        if (m_nShowCount < m_dataList.size()) // 显示的数量小于总数量
        {
            m_pScrollBar->setVisible(true); // 滚动条显示出来
        }
    }
}

int VirtualList::CalcTextWidth(const QFont& font, const QString& text)
{
    QFontMetrics fm(font);
    return fm.horizontalAdvance(text);
}

void VirtualList::RefreshData()
{
    int showCount = m_nShowCount; // 界面最终展示多少个

    // 最后数据小于应该显示的数量时,重新计算下数量
    if (showCount + m_nCurrentIndex >= m_dataList.size())
    {
        showCount = m_dataList.size() - m_nCurrentIndex;
    }
    if (showCount > 0)
    {
        int diff = std::abs(showCount - m_pStdModel->rowCount());
        if (showCount > m_pStdModel->rowCount()) // 展示的数量大于当前的,要添加
        {
            for (int i = 0; i < diff; i++)
            {
                QStandardItem* pItem = new QStandardItem;
                pItem->setSizeHint(QSize(0, nStdItemHeight));
                m_pStdModel->appendRow(pItem);
            }
        }
        else if (showCount < m_pStdModel->rowCount()) // 展示的数量小于当前的,要删除
        {
            for (int i = m_pStdModel->rowCount() - 1; i >= showCount; i--)
            {
                QStandardItem* item = m_pStdModel->item(i, 0);  // 获取QStandardItem对象
                delete item;  // 释放QStandardItem对象
                item = nullptr;
                m_pStdModel->removeRow(i);
            }
        }
        else // 等于则不动
        {
            ;
        }
        for (int i = 0; i < showCount; i++)
        {
            QStandardItem* pItem = m_pStdModel->item(i);
            pItem->setSizeHint(QSize(CalcTextWidth(pItem->font(), m_dataList[m_nCurrentIndex + i]),
                nStdItemHeight));
            pItem->setText(m_dataList[m_nCurrentIndex + i]);
        }
    }
}

int VirtualList::RowCount() const
{
    return m_dataList.size();
}

离线

楼主 #1 2024-09-16 20:49:18

memory
会员
注册时间: 2021-08-11
已发帖子: 487
积分: 464

Re: Qt5 QListView 动态加载数据

离线

楼主 #2 2024-09-16 21:28:35

memory
会员
注册时间: 2021-08-11
已发帖子: 487
积分: 464

Re: Qt5 QListView 动态加载数据

https://blog.csdn.net/qq_41359157/article/details/125163480

要实现手机类似手指滑动效果,或者其他触控屏滑动效果,需要在QTableWidget、QTableView、QListWidget、QListView控件基础上添加:

QScroller *pScroller = QScroller::scroller(listview);
pScroller->grabGesture(listview,QScroller::LeftMouseButtonGesture);
//  垂直方向按照像素的形式来滑动
listview->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
// 水平滑动条按照像素来滑动
// listview->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
// listview->setVerticalScrollMode(QListWidget::ScrollPerPixel);

离线

页脚

工信部备案:粤ICP备20025096号 Powered by FluxBB

感谢为中文互联网持续输出优质内容的各位老铁们。 QQ: 516333132, 微信(wechat): whycan_cn (哇酷网/挖坑网/填坑网) service@whycan.cn