TreeView in tkinter freez while updating

Question:

I am trying to update a treeview every second from a database with clients and ping time status,
if the successful ping was before some:X time the row will be red(or odd red to distinguish).

update_tree always is running with and sleeping 1 sec between each loop.
Also instead of deleting all the children i insert to each obj iid the values and tags

Here is update_tree function:

def update_tree(self, tree):
    index = {'id': 0, 'addr': 1, 'ping': 2}
    self.iid_count = 0
    while True:
        count = 0
        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 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)

Asked By: ניר

||

Answers:

The problem:

After having some more experience with ui’s (in Qt though)
I can clearly see that there are two main reasons why the window freezes for each update.

  1. (And this is probably the big one) You should never call time.sleep while running an event loop (tkinter have an event loop just like any other ui framework) if you do so, the application just wont handle events therefore will freeze
  2. calling a db might also block the event loop for noticeable time (especially if its on the network or a big query) although in this case it was probably very short since i was running a local sqlite server and very small queries.

The solution:

As suggested by @derek I need to drop the while loop in the function ans call it with after to add this to the event loop like so:

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 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)
Answered By: ניר
Categories: questions Tags: , , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.