更新时 tkinter freez 中的 TreeView
TreeView in tkinter freez while updating
我正在尝试每秒从具有客户端和 ping 时间状态的数据库中更新树视图,
如果 ping 成功是在 some:X 时间之前,该行将为红色(或奇数红色以区分)。
update_tree
总是 运行 并且在每个循环之间休眠 1 秒。
另外,我没有删除我插入到每个对象 iid
的所有子项,而是值和标签
这里是update_tree函数:
def update_tree(self, tree):
index = {'id': 0, 'addr': 1, 'ping': 2}
self.iid_count = 0
while True:
count = 0
ping_update_bool = self.pinger.ping_clients_db()
clients = DB().get_curs().execute("SELECT ID,ADDR,PING FROM CLIENTS").fetchall()
childrens = tree.get_children('')
# update whole treeview if bool table or bool ping true or no childrens
# if (DB().client_table_bool or ping_update_bool or childrens==()):
if childrens == ():
for client in clients:
tree.insert('', END, iid=client[index['id']], text='', values=(
"CON", client[index['ping']], client[index['addr']], client[index['id']], self.iid_count),
tags=('blue',))
self.iid_count += 1
self.db_row_count = DB().get_curs().execute("SELECT COUNT(*) FROM CLIENTS").fetchone()
else:
for client in clients:
client_ping_time = self.pinger.str_to_time(client[index['ping']])
# even rows
if count % 2 == 0:
# if the time in db for ping + 5 sec is bigger than now meaning it was created in past 3 secs
if (client_ping_time + timedelta(seconds=3) > datetime.now()):
tree.item(client[index['id']], values=(
"CON", client[index['ping']], client[index['addr']], client[index['id']], count),
tags=('seccsess_even',))
# if last seccsessful ping was before 5 sec
else:
tree.item(client[index['id']], values=(
"CON", client[index['ping']], client[index['addr']], client[index['id']], count),
tags=('fail_even',))
# odd rows
else:
if (client_ping_time + timedelta(seconds=3) > datetime.now()):
tree.item(client[index['id']], values=(
"CON", client[index['ping']], client[index['addr']], client[index['id']], count),
tags=('seccsess_odd',))
else:
tree.item(client[index['id']], values=(
"CON", client[index['ping']], client[index['addr']], client[index['id']], count),
tags=('fail_odd',))
count += 1
self.db_row_count = DB().get_curs().execute("SELECT COUNT(*) FROM CLIENTS").fetchone()
time.sleep(1)
问题:
在使用 ui 有了更多经验之后(虽然在 qt 中)
我可以清楚地看到 window 每次更新都冻结的主要原因有两个。
- (这可能是最大的)你不应该调用
time.sleep
而 运行 一个事件循环(tkinter 有一个事件循环就像任何其他 ui 框架)如果你这样做,应用程序不会处理事件,因此会冻结
- 调用数据库也可能会在很长一段时间内阻塞事件循环(特别是如果它在网络上或一个大查询),尽管在这种情况下它可能很短,因为我是 运行 本地 sqlite 服务器和非常小的查询。
解决方法:
按照@derek的建议,我需要在函数中删除 while 循环并使用 after
调用它以将其添加到事件循环中,如下所示:
iid_count = 0
index = {'id': 0, 'addr': 1, 'ping': 2}
def update_tree(self, tree):
count = 0
ping_update_bool = self.pinger.ping_clients_db()
clients = DB().get_curs().execute("SELECT ID,ADDR,PING FROM CLIENTS").fetchall()
childrens = tree.get_children('')
# update whole treeview if bool table or bool ping true or no childrens
# if (DB().client_table_bool or ping_update_bool or childrens==()):
if childrens == ():
for client in clients:
tree.insert('', END, iid=client[self.index['id']], text='', values=(
"CON", client[self.index['ping']], client[self.index['addr']], client[self.index['id']], self.iid_count),
tags=('blue',))
self.iid_count += 1
self.db_row_count = DB().get_curs().execute("SELECT COUNT(*) FROM CLIENTS").fetchone()
else:
for client in clients:
client_ping_time = self.pinger.str_to_time(client[self.index['ping']])
# even rows
if count % 2 == 0:
# if the time in db for ping + 5 sec is bigger than now meaning it was created in past 3 secs
if (client_ping_time + timedelta(seconds=3) > datetime.now()):
tree.item(client[self.index['id']], values=(
"CON", client[self.index['ping']], client[self.index['addr']], client[self.index['id']], count),
tags=('seccsess_even',))
# if last seccsessful ping was before 5 sec
else:
tree.item(client[self.index['id']], values=(
"CON", client[self.index['ping']], client[self.index['addr']], client[self.index['id']], count),
tags=('fail_even',))
# odd rows
else:
if (client_ping_time + timedelta(seconds=3) > datetime.now()):
tree.item(client[self.index['id']], values=(
"CON", client[self.index['ping']], client[self.index['addr']], client[self.index['id']], count),
tags=('seccsess_odd',))
else:
tree.item(client[self.index['id']], values=(
"CON", client[self.index['ping']], client[self.index['addr']], client[self.index['id']], count),
tags=('fail_odd',))
count += 1
self.db_row_count = DB().get_curs().execute("SELECT COUNT(*) FROM CLIENTS").fetchone()
tree.after(1000, self.update_tree, tree)
我正在尝试每秒从具有客户端和 ping 时间状态的数据库中更新树视图, 如果 ping 成功是在 some:X 时间之前,该行将为红色(或奇数红色以区分)。
update_tree
总是 运行 并且在每个循环之间休眠 1 秒。
另外,我没有删除我插入到每个对象 iid
的所有子项,而是值和标签
这里是update_tree函数:
def update_tree(self, tree):
index = {'id': 0, 'addr': 1, 'ping': 2}
self.iid_count = 0
while True:
count = 0
ping_update_bool = self.pinger.ping_clients_db()
clients = DB().get_curs().execute("SELECT ID,ADDR,PING FROM CLIENTS").fetchall()
childrens = tree.get_children('')
# update whole treeview if bool table or bool ping true or no childrens
# if (DB().client_table_bool or ping_update_bool or childrens==()):
if childrens == ():
for client in clients:
tree.insert('', END, iid=client[index['id']], text='', values=(
"CON", client[index['ping']], client[index['addr']], client[index['id']], self.iid_count),
tags=('blue',))
self.iid_count += 1
self.db_row_count = DB().get_curs().execute("SELECT COUNT(*) FROM CLIENTS").fetchone()
else:
for client in clients:
client_ping_time = self.pinger.str_to_time(client[index['ping']])
# even rows
if count % 2 == 0:
# if the time in db for ping + 5 sec is bigger than now meaning it was created in past 3 secs
if (client_ping_time + timedelta(seconds=3) > datetime.now()):
tree.item(client[index['id']], values=(
"CON", client[index['ping']], client[index['addr']], client[index['id']], count),
tags=('seccsess_even',))
# if last seccsessful ping was before 5 sec
else:
tree.item(client[index['id']], values=(
"CON", client[index['ping']], client[index['addr']], client[index['id']], count),
tags=('fail_even',))
# odd rows
else:
if (client_ping_time + timedelta(seconds=3) > datetime.now()):
tree.item(client[index['id']], values=(
"CON", client[index['ping']], client[index['addr']], client[index['id']], count),
tags=('seccsess_odd',))
else:
tree.item(client[index['id']], values=(
"CON", client[index['ping']], client[index['addr']], client[index['id']], count),
tags=('fail_odd',))
count += 1
self.db_row_count = DB().get_curs().execute("SELECT COUNT(*) FROM CLIENTS").fetchone()
time.sleep(1)
问题:
在使用 ui 有了更多经验之后(虽然在 qt 中) 我可以清楚地看到 window 每次更新都冻结的主要原因有两个。
- (这可能是最大的)你不应该调用
time.sleep
而 运行 一个事件循环(tkinter 有一个事件循环就像任何其他 ui 框架)如果你这样做,应用程序不会处理事件,因此会冻结 - 调用数据库也可能会在很长一段时间内阻塞事件循环(特别是如果它在网络上或一个大查询),尽管在这种情况下它可能很短,因为我是 运行 本地 sqlite 服务器和非常小的查询。
解决方法:
按照@derek的建议,我需要在函数中删除 while 循环并使用 after
调用它以将其添加到事件循环中,如下所示:
iid_count = 0
index = {'id': 0, 'addr': 1, 'ping': 2}
def update_tree(self, tree):
count = 0
ping_update_bool = self.pinger.ping_clients_db()
clients = DB().get_curs().execute("SELECT ID,ADDR,PING FROM CLIENTS").fetchall()
childrens = tree.get_children('')
# update whole treeview if bool table or bool ping true or no childrens
# if (DB().client_table_bool or ping_update_bool or childrens==()):
if childrens == ():
for client in clients:
tree.insert('', END, iid=client[self.index['id']], text='', values=(
"CON", client[self.index['ping']], client[self.index['addr']], client[self.index['id']], self.iid_count),
tags=('blue',))
self.iid_count += 1
self.db_row_count = DB().get_curs().execute("SELECT COUNT(*) FROM CLIENTS").fetchone()
else:
for client in clients:
client_ping_time = self.pinger.str_to_time(client[self.index['ping']])
# even rows
if count % 2 == 0:
# if the time in db for ping + 5 sec is bigger than now meaning it was created in past 3 secs
if (client_ping_time + timedelta(seconds=3) > datetime.now()):
tree.item(client[self.index['id']], values=(
"CON", client[self.index['ping']], client[self.index['addr']], client[self.index['id']], count),
tags=('seccsess_even',))
# if last seccsessful ping was before 5 sec
else:
tree.item(client[self.index['id']], values=(
"CON", client[self.index['ping']], client[self.index['addr']], client[self.index['id']], count),
tags=('fail_even',))
# odd rows
else:
if (client_ping_time + timedelta(seconds=3) > datetime.now()):
tree.item(client[self.index['id']], values=(
"CON", client[self.index['ping']], client[self.index['addr']], client[self.index['id']], count),
tags=('seccsess_odd',))
else:
tree.item(client[self.index['id']], values=(
"CON", client[self.index['ping']], client[self.index['addr']], client[self.index['id']], count),
tags=('fail_odd',))
count += 1
self.db_row_count = DB().get_curs().execute("SELECT COUNT(*) FROM CLIENTS").fetchone()
tree.after(1000, self.update_tree, tree)