Python GUI wrapper for a long operation

O

Oleg Paraschenko

Hello,

maybe of some interest:

A complete Python Tkinter sample application for a long operation
http://uucode.com/texts/pylongopgui/pyguiapp.html

A complete Python Tkinter application demonstrates one of the ways
to implement a GUI wrapper for a long operation. The long operation
works in a separated thread without frozing a GUI. A progress bar
visializes a progress in calculations. A notification widget displays
log messages. User can cancel the operation. Log messages are stored
in a viewable history buffer.

Regards, Oleg
 
S

Stewart Midwinter

good stuff!

I'll study your app for some clues for my own situation. I want to
continually get data from a remote server, and then graph values that
I have received, probably using Tkinter canvas widget. Understanding
threads will be helpful for me, so I'll study how you used threads in
your app.

You may want to post your idea on the tkinter mailing list,
thanks,
Stewart in Calgary.
 
O

Oleg Paraschenko

Hello Stewart,

(e-mail address removed) (Stewart Midwinter) wrote in message
good stuff!

I'll study your app for some clues for my own situation. I want to
continually get data from a remote server, and then graph values that
I have received, probably using Tkinter canvas widget. Understanding
threads will be helpful for me, so I'll study how you used threads in
your app.

I hope that the app will help you. Please beware that
documentation describes how things are done, but does not explain
why. It is so because some advanceed knowledge is assumed:

* threads,
* master-view-controller pattern (it is partially used in the code),
* logging in a log4j style.
You may want to post your idea on the tkinter mailing list

Thanks for the suggestion.

Regards, Oleg
 
C

Cameron Laird

Hello Stewart,

(e-mail address removed) (Stewart Midwinter) wrote in message


I hope that the app will help you. Please beware that
documentation describes how things are done, but does not explain
why. It is so because some advanceed knowledge is assumed:

* threads,
* master-view-controller pattern (it is partially used in the code),
* logging in a log4j style.
.
.
.
Note that it's not necessary to belong to the tkinter mailing list,
to post there (although the administrator has been *very* slow dur-
ing the last week about approving legitimate posts).

Also note that, under at least some circumstances, it is also *not*
necessary to rely on threads to keep a Tkinter GUI "live" during
long-running operations. For now, I assert this without demonstra-
tion, simply because I can't make the time immediately to write up
a comprehensible and simple example.
 
J

Jeff Epler

To interleave calculation and GUI activity in the same thread, simply
call the "update_idletasks" or "update" method on any Tk widget at
intervals.

update_idletasks() will do things like repaint windows that have been
uncovered or resized, or when the information displayed on a widget has
changed.

update() will handle all events, just like mainloop(), but returns as soon
as there's no longer an event to handle.

Jeff

from Tkinter import *
import time

class StupidProgress(Label):
def __init__(self, master):
Label.__init__(self, master, text="0%")

def progress(self, percent):
self.configure(text="%s%%" % percent)
self.update()

abort = 0
def calculation():
b.configure(state=DISABLED)
c.configure(state=NORMAL)
for i in range(100):
print "calculation"
time.sleep(.1) # represents doing .1 second of calculation
if check_abort():
b.configure(state=NORMAL)
c.configure(state=DISABLED)
l.progress(0)
return
l.progress(i)
l.progress(100)
c.configure(state=DISABLED)
m.configure(text="Result: 42")

def check_abort():
global abort
if not abort: return 0
abort = 0
return 1

def do_abort():
global abort
abort = 1

app = Tk()
l = StupidProgress(app)
m = Label(app, text="(No result)")
b = Button(app, command=calculation, text="Find answer")
c = Button(app, command=do_abort, text="Never mind", state=DISABLED)

l.pack()
m.pack()
b.pack()
c.pack()

app.mainloop();
 
C

Cameron Laird

To interleave calculation and GUI activity in the same thread, simply
call the "update_idletasks" or "update" method on any Tk widget at
intervals.
.
[much useful detail]
.
.
Yes and no.

First, I want readers to know that they're lucky. Jeff's
*good* with Tkinter, among other things Pythonic, and I
always seek out his postings for their valuable content.

I have particular experience that obliges me to supplement
his counsel in specialized areas. The proposition above
is slightly fragile in the vicinity of "simply". Some
developers never seem to "get the hang" of update().
Update() itself has subtleties, and a significant number
of developers like to rework our designs to avoid it; as
it turns out, it's possible and desirable in general to
rewrite update() uses in terms of transformed, event-oriented
segments. Finally, there are some "long operation"
that simply are not compatible with the Tcl-based event-
orientation behind update().

While I'm sure Fredrik has written on at least some of these
matters, I'll not make the time now to track down his essays.
For the moment, I recommend the Tcl-targeted <URL:
http://wiki.tcl.tk/1526 >, which I think those with an
interest in intermediate Tkinter topics find readable.
 
J

Jeff Epler

Cameron is right. In my other life, I frequently curse at places in
code where a programmer has added a call to "update" for the wrong
reason--usually, the code I run into needs to know the size of a newly
created widget, and "update" followed by "wm geometry" or "winfo" was
regarded as the way to get it. It doesn't happen so much any more,
over the past few *years* I've rooted out most of the bad calls, or more
frequently converted them to "update idletasks" because it's easier than
completely fixing the code.

Using the word "simply" does a disservice to the potential subtleties
of this approach, which include race conditions (they're not just for
threaded programs!) or a growing stack. (where "update" calls a binding
which calls "update" to arbitrary levels of nesting).

In my example program, I made sure that the "start calculation" button
was always disabled after it was pressed and before a call to "update",
so there's no race condition there. The "interrupt calculation" button is
only disabled during calculation (and it's the only thing enabled during
that time), and it doesn't call "update", so there's no arbitrary nesting.

For any non-trivial program, you'll get this thought process wrong.
Heck, I wouldn't be surprised to hear that I got it wrong in this case,
despite the apparent simplicity. However, even if you make the
calculation run in an event-driven way, or using threads (or abusing
generators to act as coroutines), there's still something modal about
"a long-running calculation is happening now", and it takes care to
make sure the user can never do something inappropriate for the program
in its current state.

Jeff
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,537
Members
45,021
Latest member
AkilahJaim

Latest Threads

Top