python treeview 多线程下表格插入速度慢解决方法
python treeview 多线程下表格插入速度慢解决方法
最近用tk得treeview做了个表格来展视数据
输入id,查询接口数据,然后显示数据
按照之前得操作,搜索按钮绑定一个单独得线程进行操作,这样界面不会卡死,毕竟查询网络接口是个耗时操作
大概是这样得代码
from tkinter import ttk from tkinter import * import random import threading import time class MyThread(threading.Thread): def __init__(self, func, *args): super(MyThread, self).__init__() self.func = func self.args = args self.setDaemon(True) self.start() # 在这里开始 def run(self): self.func(*self.args) def add_row():
#模拟网络请求 time.sleep(3) for i in range(300): treeview.insert('', i, values=( i, random.randint(1,10), random.randint(1,10), random.randint(1,10), random.randint(1,10), random.randint(1,10))) def del_table(): [treeview.delete(item) for item in treeview.get_children()] root = Tk() column_names = [ f'列{x}' for x in range(6)] columns = list('abcdefghijklmnopqrstuvwxyz'[:len(column_names)]) ybar = Scrollbar(root, orient='vertical') treeview = ttk.Treeview(root, height=18, show="headings", columns=columns, yscrollcommand=ybar.set) # 表格 ybar['command'] = treeview.yview ybar.pack(side=RIGHT, fill=BOTH) for num, column in enumerate(columns): treeview.column(column, width=100, anchor='center') treeview.heading(column, text=column_names[num]) treeview.pack(fill=BOTH) newb = ttk.Button(root, text='添加行', width=20, command=lambda: MyThread(add_row, )) newb.pack(fill=BOTH) newb = ttk.Button(root, text='删除表格', width=20, command=del_table) newb.pack(fill=BOTH) root.mainloop() # 进入消息循环
点击添加行按钮,3秒后可以看出在插入行,300行要插入个10来秒,插入得时候可以看到滚动条滑块再不停得变小
如果不使用线程,按钮直接绑定函数
比如
newb = ttk.Button(root, text='添加行', width=20, command=add_row)
那么在3秒的休眠中,界面会卡死,无法操作
不过插入行却是一瞬间的事情,几乎1秒不到就插入了300多行
网上查了下,说是不建议在多线程里面操作界面,会出现并发操作问题
而且这个也证明了在线程中进行大量界面修改速度是非常慢的
那么这个解决办法就是不在线程中插入表格,再ui的主线程中插入表格,这样就能做到快速插入
使用工具是queue列队,
还有tk中的after方法,TKinter的root下有个after方法, 这个方法是个定时方法,用途是GUI启动后定时执行一个方法,如果我们在被after调用的方法里再次调用这个after方法,
就可以实现一个循环效果,但这个循环不像while那样会阻塞住主线程
完整代码如下
from tkinter import ttk from tkinter import * import random import threading import time import queue table_queue = queue.Queue() class MyThread(threading.Thread): def __init__(self, func, *args): super(MyThread, self).__init__() self.func = func self.args = args self.setDaemon(True) self.start() # 在这里开始 def run(self): self.func(*self.args) def add_row(): if not table_queue.empty(): rows = table_queue.get() for row in rows: treeview.insert('', row[0], values=( row[0], row[1], row[2], row[3], row[4], row[5])) root.after(100, add_row) #运行完后再次加入到循环中 def do_add(): #模拟网络请求耗时操作 time.sleep(3) rows = [ [x,random.randint(1,10),random.randint(1,10),random.randint(1,10),random.randint(1,10),random.randint(1,10)] for x in range(300) ] table_queue.put(rows) def del_table(): [treeview.delete(item) for item in treeview.get_children()] root = Tk() root.after(100, add_row) #启动添加表格操作(不会阻塞主线程) column_names = [ f'列{x}' for x in range(6)] columns = list('abcdefghijklmnopqrstuvwxyz'[:len(column_names)]) ybar = Scrollbar(root, orient='vertical') treeview = ttk.Treeview(root, height=18, show="headings", columns=columns, yscrollcommand=ybar.set) # 表格 ybar['command'] = treeview.yview ybar.pack(side=RIGHT, fill=BOTH) for num, column in enumerate(columns): treeview.column(column, width=100, anchor='center') treeview.heading(column, text=column_names[num]) treeview.pack(fill=BOTH) newb = ttk.Button(root, text='添加行', width=20, command=lambda: MyThread(do_add, )) newb.pack(fill=BOTH) newb = ttk.Button(root, text='删除表格', width=20, command=del_table) newb.pack(fill=BOTH) root.mainloop() # 进入消息循环