Qt 大数据列表展示

Qt中使用QListWidget, QTableWidget,QTreeWidget(只考虑最大3层)自定义子widget来展示数据的时候,通常子widget的个数达到了上千加载展示就会很慢,而且很耗内存。原因是new出来的widget太多了。下面的解决方案希望能帮助你。

原理:

其实一个列表展示给用户看的高度是很有限的不会超过一个屏幕的高度,而这个高度只需要很少的子widget就可以填充满,所以,当你有1万个数据要展示时,并不需要每个数据都new一个widget(自定义的)来展示,你只需要new显示出来的那几个widget,当滚动条滚动的时候将超出屏幕的widget隐藏起来,将要新展示的数据重用隐藏的widget来展示而不需要new新的widget,只有当widget个数不足以覆盖列表显示的区域时才new新的(最多也就覆盖一个屏幕需要的个数)

组件组成

  • 一个父widget,容纳子widget和滚动条
  • 子widget使用move方法填充满展示区域
  • 滚动条加载更多内容

基本方法

  • 在showEvent里面刷新要展示的数据到widget
  • resizeEvent的时候会改变展示区域的大小需要重新刷新数据到widget使其新的区域能完全被widget展示出来
  • wheelEvent需要改变滚动条当前值
  • 连接QScrollBar::valueChanged事件,当事件发生时要将新的数据展示到widget上

怎样重用子widget

QList<ItemWidget*> m_widgets;

m_widgets来缓存所有创建出来的子widget,当需要新widget的时候看缓存里是否有隐藏的,如果有就拿出来进行新数据的展示,如果没有就根据数据的类型来创建新的widget

ItemWidget *TreeWidget::getWidget(const ItemInfo &info)
{
    // 存在这种类型的widget,并且没有被使用(不可见)直接返回
    ItemWidget *widget = nullptr;
    for (int i=0; i<m_widgets.size(); i++) {
        if (m_widgets[i]->data().type == info.type && !m_widgets[i]->isVisible()) {
            widget = m_widgets[i];
            break;
        }
    }

    if (widget == nullptr) {
        if (info.type == Top) {
            widget = new TopWidget(this);
        } else if (info.type == Parent) {
            widget = new ParentWidget(this);
        } else if (info.type == Child) {
            widget = new ChildWidget(this);
        } else {
            Q_ASSERT(0);
        }
        connect(widget, &ItemWidget::sigMousePress, [this, widget]() {
            if (widget->data().type != Child) {
                ItemInfo newInfo = widget->data();
                newInfo.isExpand = !newInfo.isExpand;
                updateItemInfo(newInfo);
                refreshWidgets();
            } else {
                gotoSelected(widget->data().id);
            }
            emit sigItemMousePress(widget->data());
        });
        connect(widget, &ItemWidget::sigMouseDoubleClick, [this, widget]() {
            emit sigItemMouseDoubleClick(widget->data());
        });
        m_widgets.append(widget);
    }

    widget->setData(info);
    // 如果是当前选中的widget
    if (widget->data().type == Child) {
        widget->setSelected(widget->data().id == m_curChildId);
    }
    widget->resize(this->width(), widget->height());
    widget->show();
    return widget;
}

###重点是刷新数据到widget上###

首先,将所有可见widget隐藏起来,遍历所有数据,y轴从0开始每次增加遍历数据展示时需要的高度,当y值大于滚动条值并且小于组件高度时就需要将子widget展示出来
组件高度就是滚动条page step值
滚动条mininum为0,maxinum为y - pageStep
公式:document length = maxinum() - minimum() + pageStep()

void TreeWidget::refreshWidgets()
{
    for (int i=0; i<m_widgets.size(); i++) {
        m_widgets[i]->resize(this->width(), m_widgets[i]->height());
        m_widgets[i]->hide();
    }

    auto moveItem = [this](const ItemInfo &item, int &y, int &startPos, bool &isContinue) {
        if (y >= m_scrollbar->value() && isContinue) {
            getWidget(item)->move(0, startPos);
            startPos += item.height;
            isContinue = startPos < this->height();
        }
        y += item.height;
    };

    int y = 0, startPos = 0;
    bool isContinue = true;
    for (int i=0; i<m_list.count(); i++) {
        const ItemInfo &topItem = m_list[i];
        moveItem(topItem, y, startPos, isContinue);
        if (topItem.childList.count() > 0 && topItem.isExpand) {
            for (int j=0; j<topItem.childList.count(); j++) {
                const ItemInfo &parentItem = topItem.childList[j];
                moveItem(parentItem, y, startPos, isContinue);
                if (parentItem.childList.count() > 0 && parentItem.isExpand) {
                    for (int k=0; k<parentItem.childList.count(); k++) {
                        const ItemInfo &childItem = parentItem.childList[k];
                        moveItem(childItem, y, startPos, isContinue);
                    }
                }
            }
        }
    }

    qDebug() << "y:" << y << ", startPos:" << startPos << ", height:" << this->height();
    m_scrollbar->move(this->width() - m_scrollbar->width(), 0);
    m_scrollbar->resize(m_scrollbar->width(), this->height());
    m_scrollbar->setPageStep(this->height());
    if (y > startPos) {
        m_scrollbar->setMaximum(y - m_scrollbar->pageStep());
        m_scrollbar->show();
        m_scrollbar->raise();
    } else {
        m_scrollbar->setMaximum(0);
        m_scrollbar->setValue(0);
        m_scrollbar->hide();
    }
    qDebug() << "min:" << m_scrollbar->minimum() << ", max:" << m_scrollbar->maximum() << ", value:" << m_scrollbar->value();
}

###结尾###

  • 性能优化
  • 响应事件扩充

源码

  • 代码中展示的元素有17000多个效率还是很高的
  • 代码中有三层也有两层,当然一层的话相信更简单
  • 红色item是最顶层
  • 绿色item是中间层
  • 白色item是最底层
    这里写图片描述
  • 5
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
### 回答1: 在Qt中进行TCP通信时,由于网络传输的不确定性和延迟,可能会导致数据在传输过程中被拆分成多个包进行发送。这种情况下,我们需要对接收到的数据进行分包处理。 Qt提供了QIODevice类及其子类来实现TCP通信,其中QIODevice::read方法用于从连接中读取数据。当接收到数据时,我们使用此方法读取一定数量的数据字节。 为了解决大数据分包的问题,我们可以采用以下步骤: 1. 连接建立后,首先监听QIODevice::readyRead信号,该信号发送表示有数据到达。 2. 在槽函数中,我们从连接中读取接收到的数据,并临时存储起来。 3. 我们需要维护一个数据缓冲区,用于存储还未处理的数据。 4. 当从连接中读取到数据时,我们将其添加到数据缓冲区中。 5. 接下来,我们需要判断数据缓冲区中是否已经接收到完整的数据包。可以通过定义特定的数据包格式,在数据中添加头部或长度来标识数据包的起始和结束位置。 6. 如果数据缓冲区中已经接收到完整的数据包,我们可以将其进行处理,并将处理后的数据传递给后续处理逻辑。 7. 如果数据缓冲区中还未接收到完整的数据包,我们需要继续等待更多的数据到达。下次有数据到达时,应将其添加到数据缓冲区的末尾。 通过以上步骤,我们可以在Qt中实现对TCP大数据的分包处理。这样可以确保正确地接收和处理完整的数据包,避免数据的丢失或混乱。 ### 回答2: 在Qt中,TCP协议是一种常用的传输协议,用于在网络上进行数据传输。TCP协议是面向连接的,可靠的,能够保证数据的有序性和完整性。然而,当发送的数据量较大时,TCP会将数据分包发送,接收端需要进行数据的拼接。 在Qt中,通过QTcpSocket类可以实现TCP通信。当需要发送大数据时,可以将数据分为多个较小的数据包进行发送。可以通过设置合适的数据包大小,使得接收端在接收数据时能够较容易地进行数据拼接。 发送端可以将大数据分割为多个较小的数据块,每个数据块的大小适当,一般不超过MTU(最大传输单元)的大小。然后将这些数据包发送给接收端,每个数据包带有一个标识,用于在接收端进行数据拼接时进行识别。 接收端接收到数据包后,需要将这些数据包进行正确的拼接才能得到完整的数据。可以通过在数据包中添加标识信息,或者根据数据包的顺序进行拼接。接收端需要维护一个缓冲区,在接收到所有数据包后,根据标识信息或顺序将数据包进行拼接,得到完整的数据。 在Qt中,可以通过QTcpSocket类的readyRead信号来接收数据,并在该信号槽函数中进行数据的处理和拼接。可以使用QByteArray类或QString类来存储和处理接收到的数据。 需要注意的是,在大数据分包的情况下,TCP协议的粘包问题也需要考虑。为了避免数据的粘包现象,可以在发送的数据包中添加特定的分隔符,或者在每个数据包的开头添加数据的大小信息等。 综上所述,在Qt中实现TCP大数据分包可以通过设置合适的数据包大小及标识信息,在发送端进行分包发送,在接收端进行数据拼接。同时,需要注意处理TCP协议可能引起的粘包问题。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值