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

一、问题描述

最近几天,我在用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)中的关键代码如下:

# 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。

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

class WorkerThread(threading.Thread):
    """ 后台线程向wxGrid加载数据 """
    def __init__(self, grid, df, rows, cols):
        threading.Thread.__init__(self)
        self.grid = grid
        self.df = df
        self.rows = rows
        self.cols = cols

    def 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的双循环语句替换成以下的代码:

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:

import wx
import wx.grid
import threading
import time


class 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 = grid

    def 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.mfbz.cn/a/659487.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

JVM学习-垃圾回收(二)

标记-清除(Mark-Sweep)算法 当堆中的有效内存空间被耗尽的时候,就会停止整个程序(stop the world),然后进行两项工作,第一项则是标记,第二项是清除 标记:Collector从引用根节点开始遍历,标记所有被引用的…

Redis分布式存储方案

一、Redis分布式存储方案 1、哈希取余分区 ①、原理 哈希计算:首先,对每个键(key)进行哈希计算,得到一个整数哈希值(hash value)。取余操作:将这个哈希值对服务器数量进行取余操作…

Ansible03-Ansible Playbook剧本详解

目录 写在前面5. Ansible Playbook 剧本5.1 YAML语法5.1.1 语法规定5.1.2 示例5.1.3 YAML数据类型 5.2 Playbook组件5.3 Playbook 案例5.3.1 Playbook语句5.3.2 Playbook1 分发hosts文件5.3.3 Playbook2 分发软件包,安装软件包,启动服务5.3.3.1 任务拆解…

数分之SQL查询电商数据案例

1,Python连接SQL数据库 以下是使用Python连接MySQL数据库并进行操作的示例代码: import random import time import pymysql# 定义名字数据 xing ["王", "李", "张", "刘", "陈", "杨", "黄&q…

【火猫CS2】fantic取代C9参加YaLLa指南针

1、近日YaLLa Compass主办方宣布,由于Could9战队未能在截止日期前提交完整的参赛阵容,fantic战队将取代其参赛。该比赛将在阿联酋阿布扎比举行,总奖金40万美元。 最近一段时间Cloud9战队最近将electroNic转会至VP,又下放了HObbit和Perfecto,队伍因没有完整阵容已被迫退出EPL S1…

服装服饰商城小程序的作用是什么

要说服装商家,那数量是非常多,厂家/经销门店/小摊/无货源等,线上线下同行竞争激烈,虽然用户群体广涵盖每个人,但每个商家肯定都希望更多客户被自己转化,渠道运营方案营销环境等不可少。 以年轻人为主的消费…

前端破圈用Docker开发项目

为什么要用 Docker 开发 🤔 直接在系统上开发不香吗?香,但是 Docker 有下面4香 环境依赖管理:Docker 容器可以管理所有依赖项,例如前端里面的 node 和 npm 版本,不需要在本地安装和维护这些依赖项 隔离&a…

【刷题(12)】图论

一、图论问题基础 在 LeetCode 中,「岛屿问题」是一个系列系列问题,比如: 岛屿数量 (Easy)岛屿的周长 (Easy)岛屿的最大面积 (Medium)最大人工岛 (Hard&…

高效记录收支明细,预设类别账户,智能统计财务脉络,轻松掌握个人财务!

收支明细管理是每位个人或企业都必须面对的财务任务,财务管理已经成为我们生活中不可或缺的一部分。如何高效记录收支明细,预设类别账户,智能统计财务脉络,轻松掌握个人财务?晨曦记账本为您提供了完美的解决方案&#…

windows环境redis未授权利用手法总结

Redis未授权产生原因 1.redis绑定在0.0.0.0:6379默认端口,直接暴露在公网,无防火墙进行来源信任防护。 2.没有设置密码认证,可以免密远程登录redis服务 漏洞危害 1.信息泄露,攻击者可以恶意执行flushall清空数据 2.可以通过ev…

使用docker安装nacos单机部署

话不多说,直接进入主题 1.查看nacos镜像 docker search nacos 一般选第一个也就是starts最高的。 2.拉取nacos镜像 docker pull nacos/nacos-serverdocker pull nacos/nacos-server:1.4.1 由于我使用的项目alibabacloud版本对应的是nacos1.4.1版本的,所以我安装的是1.4.1…

复购率下降是什么原因导致的?三个步骤直击复购率下降根源

在商业运营中,回购率的波动往往能够直观地反映出客户对品牌和产品的忠诚程度。一个健康的回购率可以为企业带来稳定的收入流,同时也是品牌口碑和市场影响力的有力证明。但是,当企业面临回购率下降的情况时,这通常是一个警示信号&a…

新版IDEA没有办法选择Java8版本解决方法

2023年11月27日后,spring.io 默认不再支持创建jdk1.8的项目 解决方法就是把 Spring的Server URL 改为阿里的。 阿里的Server URL https://start.aliyun.com/ 默认的Server URL https://start.spring.io 阿里的Server URL https://start.aliyun.com/

【iOS】UI学习(一)

UI学习(一) UILabelUIButtonUIButton事件 UIViewUIView对象的隐藏UIView的层级关系 UIWindowUIViewController定时器与视图对象 UISwitch UILabel UILabel是一种可以显示在屏幕上,显示文字的一种UI。 下面使用代码来演示UILabel的功能&#…

AI联想扩图解决方案,智能联想,无需人工干预

对于众多企业而言,无论是广告宣传、产品展示还是客户体验,高质量、宽广视野的图像都是不可或缺的。受限于车载摄像头等设备的物理限制,我们往往难以捕捉到完整、宽广的视觉场景。针对这一挑战,美摄科技凭借其前沿的AI联想扩图解决…

H2RSVLM:引领遥感视觉语言模型的革命

随着人工智能技术的飞速发展,遥感图像理解在环境监测、气候变化、粮食安全和灾害预警等多个领域扮演着越来越重要的角色。然而,现有的通用视觉语言模型(VLMs)在处理遥感图像时仍面临挑战,主要因为遥感图像的独特性和当…

15.Redis之持久化

0.知识引入 mysql的事务,有四个比较核心的特性. 1. 原子性 2.一致性 3.持久性 >(和持久化说的是一回事)【把数据存储在硬盘 >持久把数据存储茌内存上>不持久~】【重启进程/重启主机 之后,数据是否存在!!】 4.隔离性~ Redis 是一个 内存 数据库.把数据存储在内存中的…

【数据结构和算法】-动态规划爬楼梯

动态规划(Dynamic Programming,DP)是运筹学的一个分支,主要用于解决包含重叠子问题和最优子结构性质的问题。它的核心思想是将一个复杂的问题分解为若干个子问题,并保存子问题的解,以便在需要时直接利用&am…

万亿应急国债项目之通信指挥类应急装备多链路聚合通信设备在应急行业中的重要作用

万亿应急国债项目的推出,无疑是我国在应急领域的一次重大举措。在这一宏大蓝图中,通信指挥类应急装备的多链路聚合通信设备显得尤为重要,其在应急行业中所发挥的作用,堪称不可或缺的关键一环。 通信指挥是应急响应中的核心环节&a…

登峰造极,北斗相伴——纪念人类首次登顶珠穆朗玛峰71周年

71年前的今天,1953年5月29日11时30分,人类实现了一个伟大的壮举:首次登上了珠穆朗玛峰,这座海拔8848.86米的世界最高峰。这是一次充满了艰辛、勇气和智慧的探险,也是一次改变了人类历史和文化的探险。 自那以后&#…