0

I have a tkinter GUI that allows users to perform searches on a sqlite database. The database is not static so it's unknown how long each search will take. The application is single threaded (and I'd like to keep it that way). I'd therefore like a progressbar (or something animated) to re-assure the user that something is happening while the search is going on. At first glance ttk.ProgressBar looked like it might do the trick with its indeterminate mode but it doesn't seem to work as detailed in TkDocs.

For indeterminate progress TkDocs states...

...at the start of the operation you'll just call the progressbar's "start" method, and at the end of the operation, you'll call its "stop" method. The progressbar will take care of the rest.

...but that doesn't seem to be the case. If I just use .start() before I perform my sqlite query, and .stop() once it's finished, the progress bar is present but doesn't animate at all.

I can force it to update if I execute the sqlite select statement in a for loop and include a prog.step() and a prog.update_idletasks(). But this impacts performance and only really works if multiple lines are returned since the loop is for row in select_statement:.

So how can I get an indeterminate progress bar (or animation) to animate on a single thread without forcing it with step and update_idletask?


The code. This doesn't work...

prog = ttk.ProgressBar(root,mode='indeterminate')
...
prog.start()
result = None
try:
    conn = sqlite3.connect(database)
    result = conn.execute('select * from table where a=? and b=?',(var1,var2))
    result = result.fetchall()
    conn.close()
except:
    handle the exception
prog.stop()

Works-ish, but is hacky.

prog = ttk.ProgressBar(root,mode='indeterminate')
...
prog.start()
result = None
try:
    for row in conn.execute('select * from table where a=? and b=?',(var1,var2)):
        result.append(row)
        prog.step()
        prog.update_idletasks()
    conn.close()
except:
    handle the exception
prog.stop()
I_do_python
  • 1,366
  • 4
  • 16
  • 31
  • Does your program have a `.mainloop()`? – PM 2Ring Jan 22 '15 at 07:44
  • Yes. This is all happening in a `toplevel` window that the user opens from the `root` window, whose `mainloop` has already been initialised. – I_do_python Jan 22 '15 at 08:14
  • 1
    while you have stated that you would prefer to keep it single threaded, i would say that to get a smooth reacting from the GUI you would be best off making your query in a second thread, so that you can start the progressbar and let the main thread return to the mainloop. – James Kent Jan 22 '15 at 09:44
  • I agree with James. Please read [this answer](http://stackoverflow.com/a/15374078/1217270) and let me know if you are not fully enlightened. ;-] – Honest Abe Jan 23 '15 at 22:26

1 Answers1

1

So taking your second working code block and refactoring out the progress bar update cleans things up.

prog = ttk.ProgressBar(root,mode='indeterminate')
...
prog.start()

def progress():
    prog.step()
    prog.update_idletasks()

with sqlite3.connect(database) as conn:
     conn.set_progress_handler(handler=progress, n=1)
     database_query = conn.execute('select * from table where a=? and b=?',(var1,var2))
     result = database_query.fetchall()
Echocage
  • 270
  • 2
  • 4
  • 14
  • But does this have the same issue as the second example, in that `progress` will only be called when a row is found and added to the list? What would happen if a million rows were scanned and only a single row returned? Would the progress bar increment once? – I_do_python Jan 22 '15 at 09:28
  • Ah I see what you're looking for. It looks like set_progress_handler is what you're looking for. https://www.sqlite.org/c3ref/progress_handler.html I updated my answer to fit with your requirements. – Echocage Jan 22 '15 at 09:52
  • This looks very promising, but at the moment I haven't been able to get it to work as the method expects inputs other than the `progress` function, and I'm a bit stumped as to what those values should be. I'll have a play around. – I_do_python Jan 23 '15 at 09:53
  • Yeah that's really weird how hard it is to find the documentation, sorry about not linking it! [Here's the link](https://docs.python.org/3/library/sqlite3.html#sqlite3.Connection.set_progress_handler), the parameters are the handler function, then the number of iterations between each function call. I updated the code with the correct convention – Echocage Jan 25 '15 at 08:51