Qt中可视化大规模2D字符数组的最高效实现方案是什么
你之前的实现方式存在严重的冗余开销:2500万个QGraphicsSimpleTextItem每个都有独立的元对象属性、事件响应、场景排序检测逻辑,完全不需要,场景图形系统的单个元素开销很高,这种用法必然会卡顿。以下是成熟的高性能实现方案:
方案1:自定义QGraphicsItem按需绘制(适配Graphics View框架最优解)
如果需要保留Graphics View的平移、缩放、选中交互等能力,优先选这个方案,全程只需要1个图形项:
- 继承
QGraphicsItem,内部仅持有2D字符数组的只读指针,不存储任何独立图形元素 - 重写
boundingRect()返回整个字符网格的总尺寸即可 - 重写
paint()方法时,仅绘制传入参数exposedRect覆盖的可见区域对应的字符:先计算可见范围对应的行列起止序号,仅循环绘制这部分字符,完全不需要处理不可见的数组内容 - 数组更新时仅调用
update()指定变化的区域,不需要全量刷新
示例代码参考:
#include <QGraphicsItem> #include <QPainter> #include <vector> class CharGridItem : public QGraphicsItem { public: CharGridItem(const std::vector<std::vector<char>>* grid, int cellSize = 10) : m_grid(grid), m_cellSize(cellSize) { setFlag(QGraphicsItem::ItemUsesExtendedStyleOption); } QRectF boundingRect() const override { return QRectF(0, 0, m_grid->at(0).size() * m_cellSize, m_grid->size() * m_cellSize); } void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override { // 计算可见区域对应的行列范围 int startCol = qMax(0, int(option->exposedRect.left() / m_cellSize)); int endCol = qMin(int(m_grid->at(0).size()) - 1, int(option->exposedRect.right() / m_cellSize)); int startRow = qMax(0, int(option->exposedRect.top() / m_cellSize)); int endRow = qMin(int(m_grid->size()) - 1, int(option->exposedRect.bottom() / m_cellSize)); painter->setFont(QFont("Consolas", 8)); // 固定等宽字体保证对齐 for (int row = startRow; row <= endRow; ++row) { for (int col = startCol; col <= endCol; ++col) { char c = m_grid->at(row).at(col); painter->drawText(col * m_cellSize, row * m_cellSize + m_cellSize - 2, QString(c)); } } } private: const std::vector<std::vector<char>>* m_grid = nullptr; int m_cellSize = 10; };
使用时仅需要把这一个Item添加到Scene中即可,完全没有创建海量元素的开销。
方案2:QImage离屏渲染(无Graphics View依赖的轻量方案)
如果不需要缩放、选中等复杂交互,可以用更简单的轻量方案:
- 自定义普通QWidget子类,内部维护一张和视口大小匹配的QImage作为离屏画布
- 每次数组更新或视口滚动时,仅将当前可见的字符绘制到QImage上,滚动时可以复用已绘制的区域,仅增量绘制新进入视口的内容
- 重写paintEvent直接把预渲染的QImage绘制到控件上即可,绘制开销极低
额外优化建议
- 提前缓存等宽字体的metrics信息,不要每次绘制都重新计算字符宽高
- 如果字符有不同颜色样式,提前缓存QPen、QBrush对象,避免每次绘制重复创建
- 开启动画/高频更新时,给Graphics View设置OpenGL视口加速:
view.setViewport(new QOpenGLWidget());,直接用GPU完成绘制,帧率可以提升数倍 - 数组局部更新时仅刷新对应区域,不要调用全量update
内容的提问来源于stack exchange,提问作者i spin and then i lock
相关产品推荐
相关产品推荐

