Keeping a Tkinter GUI alive during a long running process

Russell E. Owen rowen at uw.edu
Wed Dec 26 15:46:53 EST 2012


In article <kb0jjh$7pm$1 at dont-email.me>,
 Kevin Walzer <kw at codebykevin.com> wrote:

> I maintain a Tkinter application that's a front-end to to a package 
> manger, and I have never been able to find a way to keep the app from 
> locking up at some point during the piping in of the package manager's 
> build output into a text widget. At some point the buffer is overwhelmed 
> and the app simply can't respond anymore, or writes data to the text 
> widget after locking up for a period.
> 
> I've long used the typical Tkinter design pattern of opening a pipe to 
> the external command, and letting it do its thing. However, after a 
> time, this locks up the app. If I try to throttle the buffer with some 
> combination of "update" or "after" or "update_idletasks," that keeps the 
> data flowing, but it comes in too slowly and keeps flowing in long after 
> the external process has terminated.
> 
> Below is a sample function that illustrates how I approach this issue. 
> Can someone suggest a better approach?
> 
>   #install a fink package
>      def installPackage(self):
> 
>          self.package = self.infotable.getcurselection()
>          if not self.package:
>              showwarning(title='Error', message='Error', detail='Please 
> select a package name.', parent=self)
>              return
>          else:
>              self.clearData()
>              self.packagename = self.package[0][1]
>              self.status.set('Installing %s' % self.packagename)
>              self.setIcon(self.phynchronicity_install)
>              self.playSound('connect')
>              self.showProgress()
>              self.file = Popen('echo %s | sudo -S %s -y install %s' % 
> (self.passtext, self.finkpath.get(), self.packagename), shell=True, 
> bufsize=0, stdout=PIPE).stdout
>              for line in self.file:
>                  self.inserturltext(line)
>                  self.after(5000, self.update_idletasks)

I suggest you use the threading or multiprocessing module: call 
subprocess.Popen in the spawned thread or process, read stdout in a 
tight loop write the result into a queue. Then have your main process 
asynchronously read the queue using polling.

It sounds a bit complicated, but in lieu of a way to asynchronously read 
the stdout pipe, I don't know what else to do that's safe.

Another option to consider is to use Twisted framework, which has its 
own support for running tasks. However, if you are not using Twisted 
already, it's a big addition.

-- Russell




More information about the Python-list mailing list