wxPython应用开发-后台线程更新大量数据到wxGrid避免ui无响应

devtools/2024/10/20 12:29:22/

一、问题描述

最近几天,我在用python开发一个数据处理的小工具。需要将xls文件中的大量数据(少则几千行多则几万行)读取出来后进行处理。其中一个功能是需要实现将读取到的原始数据和计算出来的结果在软件界面中以表格形式展示出来。

python应用开发中,我基本用wxFormBuilder进行软件界面的设计,因此用到wxPython的时候很多。wxPython中的表格类是wx.grid。这次开发过程中遇到的问题是,通过pandas将几万行数据从xls文件中读取到dataframe中后,再更新到软件主界面中的wxGrid中时,由于数据很大,导致软件ui在一段时间内无响应,一直到数据全部写入"时点库存表“中后,才会响应。

(从xls文件读取几万行数据后,向wxGrid加载数据时,出现未响应提示)

本软件中将数据从dataframe对象(self.df_kcb)写入到ui上显示为“时点库存表“的wxGrid对象(self.m_grid_kcb)中的关键代码如下:

python"># self.m_grid_kcb是wxGrid对象,self.df_kcb是dataframe对象
# 在执行下述代码之前,会首先清空self.m_grid_kcb
# 然后根据self.df_kcb中的数据行数修改self.m_grid_kcb行数,使两者保持一致for x in range(self.grid_kcb_rows):  # self.grid_kcb_rows为表格行数for y in range(6):  # 本软件中为时点库存表为6列self.m_grid_kcb.SetCellValue(x, y, str(self.df_kcb.iloc[x][y]))  # 逐单元格将数据写入wxGrid

该代码使用了双循环语句,逐个单元格的写入数据,效率不高,在遇到几万行数据时,就导致了ui无响应了。

二、解决办法

我在网上搜索相应的解决办法,找到了如下的一段话。

————————————————————————————————————————

在Python中,将大量数据写入wxPython的wx.grid.Grid控件可能会导致UI响应变慢。为了提高响应性,可以尝试以下方法:

1.分批写入数据:不要一次性将所有数据都加载到wx.grid.Grid中,而是使用分页或滚动机制,每次只加载可见的数据。

2.使用wx.lib.delayedresult:这个模块可以帮助你在一个单独的线程中处理数据的加载,从而不会阻塞UI线程。

3.避免不必要的更新:只有当数据真正改变时才更新wx.grid.Grid,避免不必要的重绘和布局操作。

————————————————————————————————————————

第一个方法,我在去年开发一款软件的时候已经用过了,那款软件中的dataframe对象内的数据量也是有上万行,采用分页显示,每次只显示当前页的几十行数据,通过导航按钮切换页面,没有出现过未响应的情况。但这次我不准备采用这个方法,因为走重复的路是无法学到新知识的。

第二个方法,在那段话后面确实附带了相关的代码示例,但是我尝试将示例融合到我的代码中,确没有成功,总是出现错误。

第三个方法嘛,则不适合当前的应用场景。因为我的应用每次都会选择加载不同的数据,数据量也不一样,必然会对wxGrid中的所有内容都进行更新。

我仍然坚持在一个页面内显示所有的数据,那么就必须解决ui无响应的问题。再次搜索的时候,我使用了“python 如何实现在后台线程中将大量数据写入wxGrid”的关键字起到了作用,百度AI给了我一个有效的答案。

这个答案中的示例直接进行测试是有效的,在想wxGrid加载数据过程中,ui界面不会出现未响应的情况。

(直接运行后,界面有响应,往下拖可以看到数据还没有更新完)

(拖动滑动条的过程中,可以看到数据在更新)

这个代码的关键是用threading模块创建后台线程,并在线程中使用wxCallAfter方法更新wxGrid

为了实现后台线程操作,要创建一个类:

python">class WorkerThread(threading.Thread):""" 后台线程wxGrid加载数据 """def __init__(self, grid, df, rows, cols):threading.Thread.__init__(self)self.grid = gridself.df = dfself.rows = rowsself.cols = colsdef run(self):for row in range(self.rows):for col in range(self.cols):wx.CallAfter(self.grid.SetCellValue, row, col, str(self.df.iloc[row][col]))

然后在我的主程序中,将之前的更新wxGrid的双循环语句替换成以下的代码:

python">self.worker = WorkerThread(self.m_grid_kcb, self.df_kcb, self.grid_kcb_rows, 6)  # 后台线程更新到grid
self.worker.start()

我参照示例修改了自己的软件,让更新wxGrid的操作在后台线程中执行,就解决了ui未响应的问题了。效果如下:

(没有出现未响应的情况,可以拖动滑条到底,表格的行数已更新,但数据还没有更新完)

(数据更新完的情况,在更新过程中,可以拖动滑条查看数据更新情况)

三、百度AI给的答案和代码

我在百度ai搜索到的文字内容和示例代码如下(代码中我添加了一点标注):

在Python中,你可以使用threading模块来创建后台线程,并在该线程中更新wxPython界面中的wxGrid。为了安全地在不同的线程之间更新GUI,你需要使用wxPython提供的wx.CallAfter方法。

以下是一个简单的例子,展示如何在后台线程中将大量数据写入wxGrid

python">import wx
import wx.grid
import threading
import timeclass MyFrame(wx.Frame):def __init__(self, parent):wx.Frame.__init__(self, parent, -1, "wxGrid Example")self.grid = wx.grid.Grid(self)# 创建并设置表格self.grid.CreateGrid(100, 2)  # 100行2列self.grid.SetRowLabelSize(-1)self.grid.SetColLabelSize(-1)# 创建并启动后台线程(这两行代码代替我之前写的双循环语句)self.worker = WorkerThread(self.grid)self.worker.start()# 这个类可以直接复制到自己的代码中后根据情况进行修改
class WorkerThread(threading.Thread):def __init__(self, grid):threading.Thread.__init__(self)self.grid = griddef run(self):# 模拟数据生成(这行代码,实际应用要替换成实际要写入wxGrid中的数据)data = [(i, i * 2) for i in range(100)]# 将数据写入wxGrid表格中(range方法中的行数和列数,按实际应用修改)for row in range(len(data)):for col in range(2):wx.CallAfter(self.grid.SetCellValue, row, col, str(data[row][col]))# 模拟耗时操作(这行代码,在实际应用中要删除)time.sleep(0.1)def main():app = wx.App(False)frame = MyFrame(None)frame.Show(True)app.MainLoop()if __name__ == '__main__':main()

在这个例子中,WorkerThread类是一个后台线程,它负责生成和处理数据。在MyFrame类的初始化方法中,创建了一个wx.grid.Grid对象,并启动了WorkerThread

WorkerThreadrun方法中,我们通过双层循环模拟了将数据填充到wxGrid中的过程。每设置完一个单元格的值后,我们使用wx.CallAfter来确保在GUI线程中执行这个更新。这样可以避免在后台线程中直接更新GUI,从而避免潜在的线程安全问题。

请注意,这个例子中的time.sleep(0.1)是模拟耗时操作,实际应用中应该去掉或替换为实际的计算或IO操作。此外,根据实际情况,你可能需要更复杂的同步机制,例如使用事件或其他同步原语来通知主线程更新GUI。

希望以上的内容,对大家有用。


http://www.ppmy.cn/devtools/43837.html

相关文章

【Go语言入门学习笔记】Part3.指针和运算符、以及基本输入

一、前言 仍然好多和C语言类似,计算机的学生应该是很容易入门这一环节,我还在最后的输入中看到了一些些Java输入的影子,而自动的变量类型推断更是有Python那个味道,正可谓几百家之所长了。 二、学习代码 package mainimport (&q…

20240529瑞芯微官方Toybrick TB-RK3588开发板的Debian11安装iperf并测试网速

20240529瑞芯微官方Toybrick TB-RK3588开发板的Debian11安装iperf并测试网速 2024/5/29 15:09 服务器端:瑞芯微官方Toybrick TB-RK3588开发板:Debian11 客户端:笔记本电脑:Ubuntu20.04 两者通过千兆交换机连接! toybr…

数据结构(三)栈 队列 数组

2024年5月26日一稿(王道P78) 栈 基本概念 基本操作 顺序存储结构 基本操作 共享栈 链式存储结构 队列 基本概念 顺序存储结构 循环队列 链式存储结构 基本操作 双端队列 栈和队列的应用 括号匹配 表达式求值 递归 层次遍历 计算机系统 数组和特殊矩阵…

QML基本语法介绍

为什么使用QML 开发者效率 将前后端分离,QML和JavaScript语言主要用于前度UI的方法,后端有C++来完成绘制。将JavaScript和C++分开能够快速迭代开发; 跨平台移植性 基于Qt平台的统一抽象概念,现在可以更加容易和快速和将Qt移植到更多的平台上。 开发的开放 Qt是由Qt-Proje…

v4l2抓取rv1126图像

0.准备工作 本文是基于正点原子的rv1126开发板使用mx415摄像头对不同节点的图像进行抓取 1.数据流向 图1 mx415采集到的数据为原始的拜尔格式(也就是raw格式),我们需要通过isp进行图像的调节才符合视觉,其中isp和ispp是两个处理的…

521源码-免费游戏源码下载-【联盟三国H5服务端】二次元卡牌封神网页手游及VM单机端

对于喜欢二次元卡牌封神网页手游的玩家们,现在有一个好消息!我们为您带来了【联盟三国H5】的全方位解析,包括其网页手游版、VM单机一键端、语音视频教程、CDK授权后台和运营后台等丰富内容。 如果您对开发游戏端感兴趣,我们特别提…

【大数据】Hadoop 2.X和1.X升级优化对比

目录 1.前言 2.hadoop 1.X的缺点和优化方向 3.解决NameNode的局限性 3.1.Hadoop HA 3.2.Haddop federation 4.yarn 5.周边组件 1.前言 本文是作者大数据系列中的一文,专栏地址: https://blog.csdn.net/joker_zjn/category_12631789.html?spm10…

Linux-等保测评部分命令

iptables防御DDOS攻击的设置 防止syn攻击(DDOOS攻击的一种) iptables -I INPUT -p tcp –syn –m limit --limit 1/s -j ACCEPT iptables -I FORWARD –p tcp --syn –m limit --limit 1/s –j ACCEPT防止各种端口扫描 iptables -A FOR…