You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为wx.grid.Grid实现分页?解决百万行CSV加载卡顿问题

解决方案:wx.grid.Grid 百万行CSV分页加载

问题背景

需要加载10列、100万行的CSV文件到wx.grid.Grid,直接全量加载并自动调整行高耗时超1小时,因此需要实现分页功能。以下提供两种思路的可运行代码,以及其他可行方案。


方案一:滚动触底自动追加行

原代码的wx.EVT_SCROLL_BOTTOM触发逻辑不够可靠,改用监听滚动窗口事件,判断滚动位置是否接近底部来触发追加。同时模拟CSV数据加载(实际使用时替换为读取CSV的逻辑):

import wx
import wx.grid
import random

class MyForm(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, parent=None, title="滚动触底分页Grid")

        panel = wx.Panel(self)
        self.myGrid = MyGrid(panel)
        self.myGrid.init_grid()

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.myGrid, 1, wx.EXPAND)
        panel.SetSizerAndFit(sizer)

        self.Maximize()

class MyGrid(wx.grid.Grid):
    def __init__(self, parent):
        wx.grid.Grid.__init__(self, parent)
        self.total_rows = 1000000  # 总数据行数
        self.page_size = 1000       # 每页行数
        self.current_rows = 0       # 当前已加载行数

    def init_grid(self):
        # 初始化列
        self.CreateGrid(0, 10)
        for col in range(10):
            self.SetColLabelValue(col, f"Column{col+1}")
        self.SetDefaultColSize(width=150, resizeExistingCols=True)
        self.SetDefaultRowSize(height=30, resizeExistingRows=True)
        
        # 加载第一页
        self.load_page()
        # 绑定滚动事件
        self.Bind(wx.EVT_SCROLLWIN, self.on_scroll)

    def load_page(self):
        # 模拟加载CSV数据,实际使用时替换为pd.read_csv或csv模块读取指定行
        if self.current_rows >= self.total_rows:
            return
        rows_to_add = min(self.page_size, self.total_rows - self.current_rows)
        self.AppendRows(rows_to_add)
        
        for row in range(self.current_rows, self.current_rows + rows_to_add):
            for col in range(10):
                self.SetCellValue(row, col, f"Row{row+1}_Col{col+1}_{random.randint(1,100)}")
        
        self.current_rows += rows_to_add

    def on_scroll(self, event):
        # 获取滚动条信息
        scroll_pos = self.GetScrollPos(wx.VERTICAL)
        scroll_range = self.GetScrollRange(wx.VERTICAL)
        scroll_thumb = self.GetScrollThumb(wx.VERTICAL)
        
        # 当滚动到距离底部小于100行时,加载下一页
        if scroll_pos + scroll_thumb >= scroll_range - 100:
            self.load_page()
        event.Skip()

if __name__ == "__main__":
    app = wx.App(False)
    frame = MyForm().Show()
    app.MainLoop()

方案二:按钮控制分页(Prev/Next)

通过按钮切换分页,每次清空当前Grid并加载对应页的数据,适合需要精准切换页码的场景:

import wx
import wx.grid
import random

class MyForm(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, parent=None, title="按钮分页Grid")

        panel = wx.Panel(self)
        self.page_num = 1           # 当前页码
        self.page_size = 1000       # 每页行数
        self.total_rows = 1000000   # 总数据行数
        self.total_pages = (self.total_rows + self.page_size - 1) // self.page_size  # 总页数

        # 分页按钮
        btn_sizer = wx.BoxSizer(wx.HORIZONTAL)
        self.prev_btn = wx.Button(panel, label="上一页")
        self.next_btn = wx.Button(panel, label="下一页")
        self.page_label = wx.StaticText(panel, label=f"页码:{self.page_num}/{self.total_pages}")
        
        btn_sizer.Add(self.prev_btn, 0, wx.ALL, 5)
        btn_sizer.Add(self.page_label, 0, wx.ALL | wx.CENTER, 5)
        btn_sizer.Add(self.next_btn, 0, wx.ALL, 5)

        # Grid组件
        self.myGrid = wx.grid.Grid(panel)
        self.init_grid()
        self.load_current_page()

        # 绑定按钮事件
        self.prev_btn.Bind(wx.EVT_BUTTON, self.on_prev)
        self.next_btn.Bind(wx.EVT_BUTTON, self.on_next)

        # 整体布局
        main_sizer = wx.BoxSizer(wx.VERTICAL)
        main_sizer.Add(btn_sizer, 0, wx.CENTER)
        main_sizer.Add(self.myGrid, 1, wx.EXPAND)
        panel.SetSizerAndFit(main_sizer)

        self.Maximize()
        # 初始化按钮状态
        self.update_btn_state()

    def init_grid(self):
        self.myGrid.CreateGrid(0, 10)
        for col in range(10):
            self.myGrid.SetColLabelValue(col, f"Column{col+1}")
        self.myGrid.SetDefaultColSize(width=150, resizeExistingCols=True)
        self.myGrid.SetDefaultRowSize(height=30, resizeExistingRows=True)

    def load_current_page(self):
        # 清空现有行
        if self.myGrid.GetNumberRows() > 0:
            self.myGrid.DeleteRows(0, self.myGrid.GetNumberRows())
        
        # 计算当前页的起始和结束行
        start_row = (self.page_num - 1) * self.page_size
        end_row = min(start_row + self.page_size, self.total_rows)
        rows_to_load = end_row - start_row

        self.myGrid.AppendRows(rows_to_load)
        # 模拟加载CSV数据,实际替换为读取指定范围的行
        for row_idx in range(rows_to_load):
            actual_row = start_row + row_idx
            for col in range(10):
                self.myGrid.SetCellValue(row_idx, col, f"Row{actual_row+1}_Col{col+1}_{random.randint(1,100)}")
        
        self.page_label.SetLabel(f"页码:{self.page_num}/{self.total_pages}")

    def on_prev(self, event):
        if self.page_num > 1:
            self.page_num -= 1
            self.load_current_page()
            self.update_btn_state()

    def on_next(self, event):
        if self.page_num < self.total_pages:
            self.page_num += 1
            self.load_current_page()
            self.update_btn_state()

    def update_btn_state(self):
        self.prev_btn.Enable(self.page_num > 1)
        self.next_btn.Enable(self.page_num < self.total_pages)

if __name__ == "__main__":
    app = wx.App(False)
    frame = MyForm().Show()
    app.MainLoop()

其他可行分页方案

  • 虚拟Grid(Virtual Grid):使用wx.grid.PyGridTableBase实现虚拟表格,只渲染当前可见的行,不需要提前加载所有数据。当滚动时,动态获取并渲染可见范围内的数据,性能最优,适合超大数据量。
  • 下拉加载更多:类似滚动触底,但在Grid底部添加一个"加载更多"的提示行,点击后追加下一页数据,比自动加载更可控。
  • 页码输入框:在按钮分页的基础上添加页码输入框,支持直接跳转到指定页码,提升操作效率。

内容的提问来源于stack exchange,提问作者tarun_b

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 04:25:39