更新时 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 每次更新都冻结的主要原因有两个。

  1. (这可能是最大的)你不应该调用 time.sleep 而 运行 一个事件循环(tkinter 有一个事件循环就像任何其他 ui 框架)如果你这样做,应用程序不会处理事件,因此会冻结
  2. 调用数据库也可能会在很长一段时间内阻塞事件循环(特别是如果它在网络上或一个大查询),尽管在这种情况下它可能很短,因为我是 运行 本地 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)