Qt QGraphicsView显示百万级数据

目录

1.使用场景

2.功能实现

(1)初始化QGraphicsView

(2) 绘制固定数量格子并分组

绘制格子

分组的原因

(3)自定义滚动条

水平滚动条

垂直滚动条

绑定滚动条

(4) 滚动时移动格子

(5)整体移动

基本逻辑

边界判断

3.运行结果

4.项目展示


1.使用场景

        项目中需要用到很多的小格子来显示数据,但是如果new很多个按钮出来的话内存就爆掉了,为了解决这个问题一开始选择使用QGraphicsView来实现,绘制的QGraphicsRectItem大大减小了内存占用,初步解决了问题。随着需求的变化,需要的格子越来越多,即使减少了内存占用数量,格子更多的时候依然会造成内存占用过多和界面卡顿,所以不得不继续优化,于是选择绘制固定的格子数量,拖动滚动条的时候通过移动格子的坐标来刷新显示。

2.功能实现

(1)初始化QGraphicsView

这里隐藏掉QGraphicsView自带的滚动条,因为会遮挡住一部分格子,使用自定义滚动条,setSceneRect决定了场景的大小,也就是实际需要的格子大小

    mCellGraphicsView = new QGraphicsView(ui->cellWidget);
    mCellGraphicsView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    mCellGraphicsScene = new QGraphicsScene(this);
    mCellGraphicsScene->setBackgroundBrush(Qt::NoBrush);
    mCellGraphicsView->setScene(mCellGraphicsScene);
    mCellGraphicsView->setAlignment(Qt::AlignLeft | Qt::AlignTop);

    mCellGraphicsScene->setItemIndexMethod(QGraphicsScene::NoIndex);
    mCellGraphicsView->setMouseTracking(true);
    mCellGraphicsView->viewport()->installEventFilter(this);
    mCellGraphicsScene->installEventFilter(this);
    mCellGraphicsView->setOptimizationFlag(QtCharts::QChartView::DontAdjustForAntialiasing, true);
    mCellGraphicsView->setViewportUpdateMode(QtCharts::QChartView::MinimalViewportUpdate);
    mCellGraphicsView->setScene(mCellGraphicsScene);
    mCellGraphicsView->setMouseTracking(true);
    mCellGraphicsView->viewport()->installEventFilter(this);

    mCellGraphicsView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    mCellGraphicsView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    mCellGraphicsView->setMaximumSize(ui->cellWidget->width(),ui->cellWidget->height());

    mCellGraphicsView->setCursor(Qt::PointingHandCursor);
    ui->verticalScrollBar->setCursor(Qt::PointingHandCursor);
    ui->horizontalScrollBar->setCursor(Qt::PointingHandCursor);

    int width=ITEM_SIZE*10000;//实际宽度
    int height=ITEM_SIZE*10000;//实际高度

    mCellGraphicsScene->setSceneRect(0, 0,width, height);

(2) 绘制固定数量格子并分组

绘制格子

这里我选择绘制6000个格子,一行100个,一列60个,一共60个组,每个组大小为10*10

    //每个组10*10共100个格子,一行10个组,一列6个组,共60个组,6000个格子
    const int gridSize = ITEM_SIZE;     // 格子大小
    const int rowCount = 10;            // 一组中一行10个格子
    const int colCount = 10;            // 一组中一列10个格子
    const int groupsRowCount = 6;        // 6行
    const int groupsColCount = 10;        // 10列

这里用二维数组列表来存储所有的组

QList<QList<QGraphicsItemGroup*>> groupList;
//改动行列数需要修改颜色list大小,否则会越界崩溃
QList<Qt::GlobalColor> rowList;
rowList<<Qt::red<<Qt::green<<Qt::blue<<Qt::cyan<<Qt::yellow<<Qt::darkBlue;
QList<Qt::GlobalColor> colList; 
colList<<Qt::darkYellow<<Qt::darkMagenta<<Qt::darkCyan<<Qt::darkBlue<<Qt::darkGreen<<Qt::darkRed<<Qt::lightGray<<Qt::color1<<Qt::gray<<Qt::blue;

//创建60个组
    for (int groupRow = 0;groupRow < groupsRowCount;groupRow++)
    {
        QList<QGraphicsItemGroup*> list;
        groupList.append(list);
        for(int groupCol = 0;groupCol< groupsColCount ;groupCol++)
        {
            QGraphicsItemGroup* group = new QGraphicsItemGroup();
            //一个组内循环
            for (int row = 0; row < rowCount; ++row)//0~9
            {
                for (int col = 0; col < colCount; ++col)//0~9
                {
                    int xPos = (groupCol * colCount + col) * (itemSize);
                    int yPos = (groupRow * rowCount + row) * (itemSize);

                    QGraphicsRectItem* rectItem = new QGraphicsRectItem(xPos, yPos, itemSize, itemSize);

                    QPen pen(QColor(0,0,0));
                    pen.setWidthF(0.5);
                    rectItem->setPen(pen);

                    //改动行列数需要修改颜色list大小,否则会越界崩溃
                    if(row==0) rectItem->setBrush(QBrush(rowList[groupRow]));
                    if(col==0) rectItem->setBrush(QBrush(colList[groupCol]));

                    group->addToGroup(rectItem);
                }
            }
            groupList[groupRow].append(group);
            mCellGraphicsScene->addItem(group);
        }
    }
分组的原因

如果整体移动的话会需要比较多的时间移动,快速拖动滚动条的时候界面就会很卡顿,所以需要分组,一次移动一行或者一列的组

(3)自定义滚动条

QScrollBar不支持直接点击跳转,重写一下这两个滚动条

水平滚动条
MyHorizontalScrollBar::MyHorizontalScrollBar(QWidget *parent) : QScrollBar(parent)
{
    int height = 12;
    setFixedHeight(height);
    QString sty = QString(R"(QScrollArea{border:none;}
                  QScrollBar:horizontal {height: 8px;background: transparent;}
                  QScrollBar::handle:horizontal {height: 8px;background: rgb(175, 205, 240);border-radius:%1px;min-width: 100px;}
                  QScrollBar::handle:horizontal:hover {height: 8px;background: rgb(165, 195, 230);border-radius:%1px;min-width: 100px;}
                  QScrollBar::add-line:horizontal {width:0px;height: 8px;subcontrol-position: right;}
                  QScrollBar::sub-line:horizontal {width:0px;height: 8px;subcontrol-position: left;}
                  QScrollBar::add-page:horizontal,QScrollBar::sub-page:horizontal {background: #E2E8F7;border-radius:%1px;})").arg(height/2);
    setStyleSheet(sty);
    setCursor(Qt::PointingHandCursor);
}
void MyHorizontalScrollBar::mousePressEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton)
    {
        //获取滑块范围
        QStyleOptionSlider opt;
        initStyleOption(&opt);
        QRect sliderHandleRect = style()->subControlRect(QStyle::CC_ScrollBar, &opt, QStyle::SC_ScrollBarSlider, this);

        if (!sliderHandleRect.contains(event->pos()))
        {
            // 获取当前点击位置,得到的这个鼠标坐标是相对于当前QSlider的坐标
            int currentX = event->pos().x();
            // 获取当前点击的位置占整个Slider的百分比
            double per = currentX * 1.0 / this->width();
            // 利用算得的百分比得到具体数字
            int value = per * (this->maximum() - this->minimum()) + this->minimum();
            // 设定滑动条位置
            setValue(value);
        }
    }
    QScrollBar::mousePressEvent(event);
}
垂直滚动条
MyVerticalScrollBar::MyVerticalScrollBar(QWidget *parent) : QScrollBar(parent)
{
    int width = 12;
    setFixedWidth(width);
    QString sty = QString(R"(    QScrollArea{border :none;}
                  QScrollBar:vertical{width:8px;background:transparent;}
                  QScrollBar::handle:vertical{width:8px;background: rgb(175, 205, 240);border-radius:%1px;min-height:100}
                  QScrollBar::handle:vertical:hover{width:8px;background: rgb(165, 195, 230);border-radius:%1px;min-height:100;}
                  QScrollBar::add-line:vertical{height:0px;width:8px;subcontrol-position:bottom;}
                  QScrollBar::sub-line:vertical{height:0px;width:8px;subcontrol-position:top;}
                  QScrollBar::add-page:vertical,QScrollBar::sub-page:vertical{background: #E2E8F7;border-radius:%1px;})").arg(width/2);
    setStyleSheet(sty);
    setCursor(Qt::PointingHandCursor);
}

void MyVerticalScrollBar::mousePressEvent(QMouseEvent * event)
{
    if (event->button() == Qt::LeftButton)
    {
        //获取滑块范围
        QStyleOptionSlider opt;
        initStyleOption(&opt);
        QRect sliderHandleRect = style()->subControlRect(QStyle::CC_ScrollBar, &opt, QStyle::SC_ScrollBarSlider, this);

        if (!sliderHandleRect.contains(event->pos()))
        {
            //获取当前点击位置,得到的这个鼠标坐标是相对于当前QSlider的坐标
            int currentY = event->pos().y();
            //获取当前点击的位置占整个Slider的百分比
            double per = currentY *1.0 /this->height();
            //利用算得的百分比得到具体数字
            int value = per*(this->maximum() - this->minimum()) + this->minimum();
            //设定滑动条位置
            setValue(value);
        }

    }
    QScrollBar::mousePressEvent(event);
}
绑定滚动条

与QGraphicsView的滚动条绑定,并关联滚动条值改变时触发slip函数

    connect(mCellGraphicsView->horizontalScrollBar(), &QScrollBar::valueChanged, this, &MainWindow::slip);
    connect(mCellGraphicsView->verticalScrollBar(), &QScrollBar::valueChanged, this, &MainWindow::slip);

    connect(ui->verticalScrollBar, &QScrollBar::valueChanged, mCellGraphicsView->verticalScrollBar(), &QScrollBar::setValue);
    connect(ui->horizontalScrollBar, &QScrollBar::valueChanged, mCellGraphicsView->horizontalScrollBar(), &QScrollBar::setValue);

    connect(mCellGraphicsView->verticalScrollBar(), &QScrollBar::rangeChanged, this, [=](int min, int max)
    {
        ui->verticalScrollBar->setRange(min, max);
        if(max>0) ui->verticalScrollBar->show();
        else ui->verticalScrollBar->hide();
    });
    connect(mCellGraphicsView->horizontalScrollBar(), &QScrollBar::rangeChanged, this, [=](int min, int max)
    {
        ui->horizontalScrollBar->setRange(min, max);
        if(max>0) ui->horizontalScrollBar->show();
        else ui->horizontalScrollBar->hide();
    });

(4) 滚动时移动格子

下滑:窗口只能显示一部分的格子,所以在滚动条下滑的时候不需要立刻移动,目的是为了让窗口一直能看到格子,所以偏移量达到一定值的时候将最上方一行的组移动到最下方填补就可以实现

上滑:需要判断目前上方还有没有格子,如果没有就会出现空白,所以当上方不足一组高度的时候需要将最底下一行的组移动到最上面,并且需要有边界判断,不能移出界

右滑:同理下滑,不需要判断边界

左滑:同理上滑,需要保证有左边有一组格子,并且要判断边界

void MainWindow::slip()
{
    int verticalCurrentY = mCellGraphicsView->verticalScrollBar()->value();
    int horizontalCurrentX = mCellGraphicsView->horizontalScrollBar()->value();

    int differenceY =  verticalCurrentY-oldVerticalCurrentY;
    int differenceX =  horizontalCurrentX-oldHorizontalCurrentX;
    int totalHeight = groupsRowCount*colCount*ITEM_SIZE; //6*10*ITEM_SIZE
    int totalWidth = groupsColCount*rowCount*ITEM_SIZE; //10*10*ITEM_SIZE

    //下滑
    if(differenceY>0)
    {
        int needToSlip = totalHeight/2; // 根据窗口大小调节,如果出现空白就调小
        while(verticalCurrentY-oldFrontY>=needToSlip && verticalCurrentY-oldFrontY<totalHeight)
        {
            slipDown();
        }
        if(verticalCurrentY-oldFrontY>=totalHeight)//大于整体格子的高度直接整体移动
        {
            graphicMoveY();//整体移动
        }
    }
    //上滑
    else if(differenceY<0)
    {
        //不够一组距离的时候补充一组
        while(verticalCurrentY-oldFrontY<=ITEM_SIZE*colCount && oldFrontY-verticalCurrentY<totalHeight && oldFrontY>0)
        {
            slipUp();
        }
        if (oldFrontY-verticalCurrentY>=totalHeight)
        {
            graphicMoveY();//整体移动
        }
    }
    //右滑
    else if(differenceX>0)
    {
        int needToSlip = totalWidth/2; // 根据窗口大小调节,如果出现空白就调小
        while(horizontalCurrentX-oldLeftX>=needToSlip && horizontalCurrentX-oldLeftX<totalWidth)
        {
            slipRight();
        }
        if (horizontalCurrentX-oldLeftX>=totalWidth)//大于整体格子的宽度直接整体移动
        {
            graphicMoveX();//整体移动
        }
    }
    //左滑
    else if(differenceX<0)
    {
        //不够一组距离的时候补充一组
        while(horizontalCurrentX-oldLeftX<(ITEM_SIZE*rowCount) && oldLeftX-horizontalCurrentX<totalWidth && oldLeftX>0)
        {
            slipLeft();
        }
        if (oldLeftX-horizontalCurrentX>=totalWidth)
        {
            graphicMoveX();//整体移动
        }
    }

    oldVerticalCurrentY = verticalCurrentY;
    oldHorizontalCurrentX = horizontalCurrentX;
}

(5)整体移动

基本逻辑

点击的滚动条坐标很有可能不是整体格子的宽度或者高度,所以需要先移动到宽度或者高度的整数倍,不然的话就会出现有空白的地方。移动到整数倍的时候也会有可能出现窗口显示出空白的地方,所以整体移动完需要判断一下是否需要移动组来补齐空白。

边界判断

如果点击的位置小于整体大小,就将整体移动到起点,不要移出去

3.运行结果

代码比较多,这里只展示大概思路和部分代码,可以下载demo的源码

https://download.csdn.net/download/weixin_64319089/89461578

4.项目展示

demo实现了界面的刷新,在界面移动的时候加上数据的刷新即可实现高性能低内存的百万级数据刷新

  • 19
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要消除Qt QGraphicsView显示的图片,可以尝试以下方法: 1. 清空场景: 使用`clear()`函数可以清空QGraphicsScene中的所有项。例如,可以调用`scene->clear()`来清空场景。 2. 删除特定项: 如果要删除特定的项,可以使用`removeItem()`函数来删除场景中的指定项。例如,可以调用`scene->removeItem(item)`来删除名为item的项。 3. 隐藏视图: 如果只是想暂时隐藏视图,可以使用`hide()`函数隐藏QGraphicsView。例如,调用`ui->graphicsView->hide()`可以隐藏QGraphicsView。 请注意,以上方法仅适用于清除或隐藏已经添加到场景或视图中的项或图片。如果您想要删除加载到QGraphicsView中的图片,您需要首先了解图片是如何加载和显示的。根据提供的引用内容,可以看到在QGraphicsView中设置了一个QGraphicsScene,并且在该场景中添加了项。因此,您可能需要查找和删除与要消除的图片相关联的项。 总结来说,要消除Qt QGraphicsView显示的图片,可以通过清空场景、删除特定项或隐藏视图来实现。具体的方法取决于您的实际需求和代码实现。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [【QtQGraphicsView与QGraphicsScene配合实现全屏并消除进度条](https://blog.csdn.net/YIZHILIUSHA2020/article/details/117163229)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [老歪用Qt C++写的读取SEGY和SEGD格式的地震数据源码,支持波形显示和变密度显示Qt5.12版本上编译通过](https://download.csdn.net/download/Idealtracy/88244376)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值