Tkinter: update_idletasks

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Jeffrey Barish

    Tkinter: update_idletasks

    I'm confused about how to use the update_idletask s method. In my
    program, I have a handler for a button in which execution will linger.
    During that time, I would like for the GUI to continue to show signs of
    life. I have a Pmw MessageBar in which I display a status message. I
    figured out that if I run update_idletask s on that MessageBar, then the
    MessageBar will update the display as I update the message. However,
    if I cover the GUI with some other window and then expose it again, the
    GUI does not refresh until the handler finishes (except for the
    MessageBar). Do I have to run the update_idletask s method for every
    widget in the GUI? for all the frames? for just the root frame? Or is
    it impossible to get the GUI to refresh in this situation?
    --
    Jeffrey Barish


  • Russell E. Owen

    #2
    Re: Tkinter: update_idletask s

    In article <mailman.110.10 85067858.6949.p ython-list@python.org >,
    Jeffrey Barish <jeffbarish@sta rband.net> wrote:
    [color=blue]
    >I'm confused about how to use the update_idletask s method. In my
    >program, I have a handler for a button in which execution will linger.
    >During that time, I would like for the GUI to continue to show signs of
    >life. I have a Pmw MessageBar in which I display a status message. I
    >figured out that if I run update_idletask s on that MessageBar, then the
    >MessageBar will update the display as I update the message. However,
    >if I cover the GUI with some other window and then expose it again, the
    >GUI does not refresh until the handler finishes (except for the
    >MessageBar). Do I have to run the update_idletask s method for every
    >widget in the GUI? for all the frames? for just the root frame? Or is
    >it impossible to get the GUI to refresh in this situation?[/color]

    To make your GUI responsive, call update_idletask s occasionally from the
    task that is taking a long time. Like most GUI systems, Tkinter is
    basically a single threaded system. It'll run the current task until
    finished, then process the next event. update_idletask s gives it a
    chance to handle other events.

    There are other ways to handle this sort of thing. Typically a
    long-running task should be run as a separate background thread (or even
    a separate process). The difficulty is that background threads cannot
    safely interact with GUI elements, so how does the thread communicate?

    The most straightforward technique is to transfer data from the
    background thread to the main thread via a Queue object (as usual), then
    poll the background thread's Queue (by repeatedly calling after to do a
    nonblocking read on the Queue object).

    However, a clever trick posted fairly recently is to have the background
    thread generate an event. Apparently that is safe. Then have a handler
    listen for that event. The handler will run in the main thread, and so
    can safely update the GUI.

    -- Russell

    Comment

    • Eric Brunel

      #3
      Re: Tkinter: update_idletask s

      Jeffrey Barish wrote:[color=blue]
      > I'm confused about how to use the update_idletask s method. In my
      > program, I have a handler for a button in which execution will linger.
      > During that time, I would like for the GUI to continue to show signs of
      > life. I have a Pmw MessageBar in which I display a status message. I
      > figured out that if I run update_idletask s on that MessageBar, then the
      > MessageBar will update the display as I update the message. However,
      > if I cover the GUI with some other window and then expose it again, the
      > GUI does not refresh until the handler finishes (except for the
      > MessageBar). Do I have to run the update_idletask s method for every
      > widget in the GUI? for all the frames? for just the root frame? Or is
      > it impossible to get the GUI to refresh in this situation?[/color]

      At tcl level, update_idletask s isn't a "method", i.e. the tcl command doesn't
      take any parameter telling which widget to refresh. So calling the Tkinter
      update_idletask s method on any widget has exactly the same effect, which is to
      refresh the whole GUI.

      There are some issues on Windows however, where the newly created toplevel's may
      not refresh until full control is returned to the GUI. To work around this
      problem, use the wait_visibility method to wait until the newly created window
      is displayed. But be careful: wait_visibility processes events, unlike
      update_idletask s that does just a GUI refresh. There is apparently no simple way
      of updating the display of newly created toplevel's on Windows without returning
      full control to the GUI (at least with tk/Tkinter)

      I never saw the problem you describe (some windows refreshing, some not), but I
      mainly develop on Linux, which may show a different behaviour than the platform
      you're working on (which BTW you don't mention...). Maybe you can post a small
      piece of code showing the problem?

      HTH
      --
      - Eric Brunel <eric (underscore) brunel (at) despammed (dot) com> -
      PragmaDev : Real Time Software Development Tools - http://www.pragmadev.com

      Comment

      • Jeffrey Barish

        #4
        Re: Tkinter: update_idletask s

        Eric Brunel wrote:
        [color=blue]
        > Jeffrey Barish wrote:[color=green]
        >> I'm confused about how to use the update_idletask s method. In my
        >> program, I have a handler for a button in which execution will
        >> linger. During that time, I would like for the GUI to continue to
        >> show signs of
        >> life. I have a Pmw MessageBar in which I display a status message.
        >> I figured out that if I run update_idletask s on that MessageBar, then
        >> the
        >> MessageBar will update the display as I update the message. However,
        >> if I cover the GUI with some other window and then expose it again,
        >> the GUI does not refresh until the handler finishes (except for the
        >> MessageBar). Do I have to run the update_idletask s method for every
        >> widget in the GUI? for all the frames? for just the root frame? Or
        >> is it impossible to get the GUI to refresh in this situation?[/color]
        >
        > At tcl level, update_idletask s isn't a "method", i.e. the tcl command
        > doesn't take any parameter telling which widget to refresh. So calling
        > the Tkinter update_idletask s method on any widget has exactly the same
        > effect, which is to refresh the whole GUI.
        >
        > There are some issues on Windows however, where the newly created
        > toplevel's may not refresh until full control is returned to the GUI.
        > To work around this problem, use the wait_visibility method to wait
        > until the newly created window is displayed. But be careful:
        > wait_visibility processes events, unlike update_idletask s that does
        > just a GUI refresh. There is apparently no simple way of updating the
        > display of newly created toplevel's on Windows without returning full
        > control to the GUI (at least with tk/Tkinter)
        >
        > I never saw the problem you describe (some windows refreshing, some
        > not), but I mainly develop on Linux, which may show a different
        > behaviour than the platform you're working on (which BTW you don't
        > mention...). Maybe you can post a small piece of code showing the
        > problem?
        >
        > HTH[/color]
        Ah. I had a hunch that might be the case. However, that is not the
        behavior that I am seeing. First, in response to your questions: (1) I
        am on Linux (there are other platforms?); (2) I am using Python 2.3;
        (3) it is difficult to extract a piece of the code, but I will attempt
        to describe more clearly what I am doing. There is a button with a
        handler. In the handler I use popen3 to launch a program that takes a
        long time to execute. I monitor its progress in a while loop by
        reading a status line that it produces on stderr. The status line
        provides information about percentage complete; I use that information
        to update the MessageBar in my GUI. When the status line indicates
        that the process is done, I exit the while loop and return from the
        button handler. I tried running update_idletask s on the MessageBar in
        the while loop. I tried running it on the root window, even though the
        information you provided indicated that it doesn't matter what class
        owns the method -- and my experience certainly does not contradict that
        statement. In every case, only the MessageBar updates. Well, that's
        not entirely true. I also move a tag in a text widget; the previously
        and newly tagged text redraws. The only technique I have found that
        permits the main window to update is to run most of the handler in a
        separate thread. The problem I am having with that approach is that
        the MessageBar then flashes in an annoying way (the background seems to
        go to white at every update and then gets redrawn to gray -- which
        happens only when the while loop is in its own thread). Any other
        thoughts would be much appreciated.
        --
        Jeffrey Barish


        Comment

        • klappnase

          #5
          Re: Tkinter: update_idletask s

          Jeffrey Barish <jeffbarish@sta rband.net> wrote in message news:<mailman.1 59.1085178672.6 949.python-list@python.org >...
          [color=blue]
          > Ah. I had a hunch that might be the case. However, that is not the
          > behavior that I am seeing. First, in response to your questions: (1) I
          > am on Linux (there are other platforms?); (2) I am using Python 2.3;
          > (3) it is difficult to extract a piece of the code, but I will attempt
          > to describe more clearly what I am doing. There is a button with a
          > handler. In the handler I use popen3 to launch a program that takes a
          > long time to execute. I monitor its progress in a while loop by
          > reading a status line that it produces on stderr. The status line
          > provides information about percentage complete; I use that information
          > to update the MessageBar in my GUI. When the status line indicates
          > that the process is done, I exit the while loop and return from the
          > button handler. I tried running update_idletask s on the MessageBar in
          > the while loop. I tried running it on the root window, even though the
          > information you provided indicated that it doesn't matter what class
          > owns the method -- and my experience certainly does not contradict that
          > statement. In every case, only the MessageBar updates. Well, that's
          > not entirely true. I also move a tag in a text widget; the previously
          > and newly tagged text redraws. The only technique I have found that
          > permits the main window to update is to run most of the handler in a
          > separate thread. The problem I am having with that approach is that
          > the MessageBar then flashes in an annoying way (the background seems to
          > go to white at every update and then gets redrawn to gray -- which
          > happens only when the while loop is in its own thread). Any other
          > thoughts would be much appreciated.[/color]

          I've been using a Tkinter filehandler for similar tasks and didn't
          have the problems you describe.
          Here's a pseudo-code snippet to illustrate what I did:

          from Tkinter import *
          import os, fcntl, popen2

          def button_callback (self, event):
          #the function that's bound to the button
          #dialog window with progress bar:
          self.pw = ProgressWindow. ProgressWindow( )
          selectedfiles = ' '.join(tracklis t)
          normalizecmd = 'exec normalize -m ' + selectedfiles
          self.pp = popen2.Popen4(n ormalizecmd)
          self.mkfilehand ler(self.pp, self.getprogres s)

          def mkfilehandler(s elf, popen4object, function):
          #creates the filehandler
          fileobject = popen4object.fr omchild
          filedescr = fileobject.file no()
          fcntl.fcntl(fil edescr, fcntl.F_SETFL, os.O_NONBLOCK)
          tkinter.createf ilehandler(file object, READABLE, function)

          def getprogress(sel f, fileobject, event_type):
          message = self.pp.fromchi ld.read()
          if message == '':
          # process has finished
          # this could be checked with "if self.pp.poll() != -1:" either
          tkinter.deletef ilehandler(self .pp.fromchild)
          <...clean up stuff here...>
          self.pp = None
          else:
          <...parse the output messages, extract progress values,
          etc....>
          # update the progress bar (update_idletas ks() is called from
          it's set() method):
          self.pw.set(val ue=progress)

          I hope this helps

          Michael

          Comment

          Working...