Command config, quitting, binary, Timer

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • bearophileHUGS@lycos.com

    #1

    Command config, quitting, binary, Timer

    Hello, I have four things to ask or to suggest, sorry if they seem
    basic or already discussed.

    -------------------

    I am still ignorant about Tkinter. This little program, after pressing
    the "Go" eats more and more RAM, is it normal? Can it be avoided? (In
    normal programs this is isn't a real problem).

    ! import Tkinter
    ! def dogo():
    ! while 1:
    ! b.config(comman d=lambda:None)
    ! root = Tkinter.Tk()
    ! b = Tkinter.Button( root, text="Go", command=dogo)
    ! b.pack()
    ! root.mainloop()

    Note: I have found this problem because in a small program I have a
    Start button that becomes a Stop, so the bound command must change each
    time. (Maybe there is some other solution, like hiding/unhiding the
    buttons, I don't know.)

    -------------------

    When I quit a Tkinter program (clicking the [X] button of the window)
    while it's running, it stops with something like this:

    TclError: invalid command name ".9970192"

    There is a way to avoid it, or to intercept that quitting command to
    stop the program more gracefully?

    -------------------

    When I have to convert numbers to binary I use something like this (the
    management of negative numbers is removed). It's my faster Python
    version (derived by a Hettinger's version):

    ! from collections import deque
    ! def binary_conv(n):
    ! if n == 0:
    ! return 0
    ! else:
    ! absn = abs(n)
    ! conv = ["0", "1"]
    ! result = deque()
    ! _app = result.appendle ft
    ! while absn:
    ! _app( conv[absn & 1] )
    ! absn >>= 1
    ! return int("".join(res ult))

    But converting to binary is a quite common operation, so I think it can
    be added as a binary function to Python, for example adding "b" to the
    the conversion types of the % formatting.

    -------------------

    Sometimes I need something like the Timer of Java, it generates "ticks"
    and calls a function for each tick. The tick frequency can be set, or
    they can be stopped.

    In another Newsgroup someone has suggested me that the callLater of
    Twisted can solve my problem, but I think it's a quite common thing, so
    I think that maybe it can be added to the threading standard module.

    This is a rough Python version of mine (without comments), it's not a
    true Metronome because it counts the delay time after the end of the
    last function call. This class also seems fragile, sometimes it gives
    me problems, and I cannot use too much concurrent metronomes, etc. It's
    quite

    Maybe someone can suggest me how to improve it.


    ! from threading import Timer
    !
    ! class Metronome(objec t):
    ! def __init__(self, interval, fun, *args, **kwargs):
    ! self.interval = interval # seconds.
    ! self.fun = fun
    ! self.args = args
    ! self.kwargs = kwargs
    ! self._running = False
    ! def _go(self):
    ! # Call the function with the stored values
    ! self.fun(*self. args, **self.kwargs)
    ! if self._running:
    ! self._call = Timer(self.inte rval, self._go)
    ! self._call.star t()
    ! def start(self):
    ! if not self._running:
    ! self._running = True
    ! self._go()
    ! def stop(self):
    ! if self._running:
    ! self._call.canc el()
    ! self._running = False

    Thank you,
    bearophile

  • Peter Hansen

    #2
    Re: Command config, quitting, binary, Timer

    bearophileHUGS@ lycos.com wrote:[color=blue]
    > I am still ignorant about Tkinter. This little program, after pressing
    > the "Go" eats more and more RAM, is it normal? Can it be avoided? (In
    > normal programs this is isn't a real problem).
    >
    > ! import Tkinter
    > ! def dogo():
    > ! while 1:
    > ! b.config(comman d=lambda:None)
    > ! root = Tkinter.Tk()
    > ! b = Tkinter.Button( root, text="Go", command=dogo)
    > ! b.pack()
    > ! root.mainloop()[/color]

    What did you expect to happen with the infinite loop inside dogo()?
    Wouldn't it call b.config() over and over again, never returning? Is
    that really what you wanted?

    -Peter

    Comment

    • Dennis Lee Bieber

      #3
      Re: Command config, quitting, binary, Timer

      On 3 Sep 2005 18:44:11 -0700, bearophileHUGS@ lycos.com declaimed the
      following in comp.lang.pytho n:
      [color=blue]
      >
      > ! import Tkinter
      > ! def dogo():
      > ! while 1:
      > ! b.config(comman d=lambda:None)[/color]

      Well, this infinite loop can't be good for memory usage (I'm no
      expert -- what does .config() do? [If I interpret the skimpy help
      system, basically this one is replacing the original "dogo" method with
      an anonymous function that "returns" None -- being in an endless loop,
      you are creating an endless number of anonymous functions; they may not
      be garbage collected until the loop ends]
      [color=blue]
      > Note: I have found this problem because in a small program I have a
      > Start button that becomes a Stop, so the bound command must change each
      > time. (Maybe there is some other solution, like hiding/unhiding the
      > buttons, I don't know.)
      >[/color]
      Would it not be easier to have a single callback, but change the
      button Label, and have the callback branch based on the current label?
      [color=blue]
      > -------------------
      >
      > When I quit a Tkinter program (clicking the [X] button of the window)
      > while it's running, it stops with something like this:
      >
      > TclError: invalid command name ".9970192"
      >
      > There is a way to avoid it, or to intercept that quitting command to
      > stop the program more gracefully?
      >[/color]
      No idea -- my only Tkinter program (at work, so I can't test it),
      doesn't do anything like this.[color=blue]
      > -------------------
      >
      > When I have to convert numbers to binary I use something like this (the
      > management of negative numbers is removed). It's my faster Python
      > version (derived by a Hettinger's version):
      >
      > ! from collections import deque
      > ! def binary_conv(n):
      > ! if n == 0:
      > ! return 0
      > ! else:
      > ! absn = abs(n)
      > ! conv = ["0", "1"]
      > ! result = deque()
      > ! _app = result.appendle ft
      > ! while absn:
      > ! _app( conv[absn & 1] )
      > ! absn >>= 1
      > ! return int("".join(res ult))
      >
      > But converting to binary is a quite common operation, so I think it can
      > be added as a binary function to Python, for example adding "b" to the
      > the conversion types of the % formatting.[/color]

      Ugh...

      And exactly what are you trying to return? That last line implies
      you are returning an integer... If I understand the logic, you are
      taking, say an integer of 3 (in binary "00000011"b , assuming only a byte
      in length), creating a string of 0/1 ("11"), and then converting this
      string back to an integer with the value 11 (binary "10000011") ...


      NYBBLES = { "0" : "0000",
      "1" : "0001",
      "2" : "0010",
      "3" : "0011",
      "4" : "0100",
      "5" : "0101",
      "6" : "0110",
      "7" : "0111",
      "8" : "1000",
      "9" : "1001",
      "A" : "1010",
      "B" : "1011",
      "C" : "1100",
      "D" : "1101",
      "E" : "1110",
      "F" : "1111" }

      def conv2b(n):
      tstr = "%X" % n
      olst = []
      for nyb in tstr:
      olst.append(NYB BLES[nyb])
      return "".join(ols t)

      print
      print "3 is ", conv2b(3)
      print "11 is ", conv2b(11)
      print "3.141 is ", conv2b(3.141)
      print "-3 is ",conv2b(-3)
      print "40558 is ", conv2b(40558)

      -=-=-=-=-=-=-
      3 is 0011
      11 is 1011
      3.141 is 0011
      E:\UserData\Den nis Lee Bieber\My Documents\Pytho n Progs\Script1.p y:20:
      FutureWarning: %u/%o/%x/%X of negative int will return a signed string
      in Python 2.4 and up
      tstr = "%X" % n
      -3 is 111111111111111 111111111111111 01
      40558 is 100111100110111 0

      I've not tested it, but I somehow suspect it is faster than a mass
      of push/pop and shifts...

      [color=blue]
      >
      > -------------------
      >
      > Sometimes I need something like the Timer of Java, it generates "ticks"
      > and calls a function for each tick. The tick frequency can be set, or
      > they can be stopped.
      >
      > In another Newsgroup someone has suggested me that the callLater of
      > Twisted can solve my problem, but I think it's a quite common thing, so
      > I think that maybe it can be added to the threading standard module.
      >
      > This is a rough Python version of mine (without comments), it's not a
      > true Metronome because it counts the delay time after the end of the
      > last function call. This class also seems fragile, sometimes it gives
      > me problems, and I cannot use too much concurrent metronomes, etc. It's
      > quite
      >[/color]
      Main problem I see with this is that each call to threading.Timer ()
      is creating a NEW thread just for one tick. Lots of overhead involved.
      I'd create a single thread whose main run method is a continuous loop,
      using time.sleep() to suspend between ticks. .Timer() is optimized for a
      one-time callback.

      Note that using _nextInterval allows me perform an adjustment for
      the time taken by the callback function.

      -=-=-=-=-=-=-
      import time
      import threading

      class Metronome(objec t):
      def __init__(self, interval, func, *args, **kwargs):
      self._func = func
      self._args = args
      self._kwargs = kwargs
      self._interval = interval
      self._running = False
      self._Timer = None
      self._nextInter val = 0

      def Start(self):
      if not self._running:
      print "Creating worker at %s" % time.time()
      self._Timer = threading.Threa d(None, self._worker, (), {})
      self._nextInter val = time.time() + self._interval
      self._running = True
      self._Timer.sta rt()

      def Stop(self):
      if self._running:
      print "Stopping worker at %s" % time.time()
      self._running = False
      self._Timer.joi n()
      print "thread joined"

      def _worker(self):
      while self._running:
      time.sleep(self ._nextInterval - time.time())
      self._nextInter val += self._interval
      self._func(*sel f._args, **self._kwargs)


      def aTick():
      print "A tick happened at: %s" % time.time()

      if __name__ == "__main__":
      m = Metronome(5.0, aTick)
      m.Start()
      time.sleep(30)
      m.Stop()
      time.sleep(2.5)
      m.Start()
      time.sleep(20)
      m.Stop()


      --[color=blue]
      > =============== =============== =============== =============== == <
      > wlfraed@ix.netc om.com | Wulfraed Dennis Lee Bieber KD6MOG <
      > wulfraed@dm.net | Bestiaria Support Staff <
      > =============== =============== =============== =============== == <
      > Home Page: <http://www.dm.net/~wulfraed/> <
      > Overflow Page: <http://wlfraed.home.ne tcom.com/> <[/color]

      Comment

      • Diez B. Roggisch

        #4
        Re: Command config, quitting, binary, Timer

        > ! import Tkinter[color=blue]
        > ! def dogo():
        > ! while 1:
        > ! b.config(comman d=lambda:None)
        > ! root = Tkinter.Tk()
        > ! b = Tkinter.Button( root, text="Go", command=dogo)
        > ! b.pack()
        > ! root.mainloop()[/color]


        I guess tkinter has to keep a name-reference pair (some days a
        discussion about this arose and /F said something like that). It
        certainly is somewhat ugly - but there might be very good reasons to do so.[color=blue]
        >
        > Note: I have found this problem because in a small program I have a
        > Start button that becomes a Stop, so the bound command must change each
        > time. (Maybe there is some other solution, like hiding/unhiding the
        > buttons, I don't know.)[/color]

        Had that to do myself - my solution was to use the buttons text for
        swich-casing. I guess you change the buttons text anyways (otherwise
        nobody would know that its semantic changed), so do something like this
        (untetsted)

        def start_stop_call back():
        if b.get_config("t ext") == "Start":
        b.config(text=" Stop")
        ....
        else:
        b.config(text=" Start")
        ....

        [color=blue]
        > Sometimes I need something like the Timer of Java, it generates "ticks"
        > and calls a function for each tick. The tick frequency can be set, or
        > they can be stopped.
        >
        > In another Newsgroup someone has suggested me that the callLater of
        > Twisted can solve my problem, but I think it's a quite common thing, so
        > I think that maybe it can be added to the threading standard module.
        >
        > This is a rough Python version of mine (without comments), it's not a
        > true Metronome because it counts the delay time after the end of the
        > last function call. This class also seems fragile, sometimes it gives
        > me problems, and I cannot use too much concurrent metronomes, etc. It's
        > quite
        >
        > Maybe someone can suggest me how to improve it.
        >[/color]

        Instead of several metronomes, use one thread and compute the gresatest
        common divisor for all scheduled timings. Use that as sleep interval
        (your "ticks"), and count ticks for each timer. You should introduce a
        lower thrshold on tick-length, otherwise you can get into trouble.


        Diez

        Comment

        • bearophileHUGS@lycos.com

          #5
          Re: Command config, quitting, binary, Timer

          Witn your suggestions and with some tests and work I've solved most of
          the problems, thank you all for the comments.

          Peter Hansen:
          [color=blue]
          >What did you expect to happen with the infinite loop inside dogo()?<[/color]

          I expected that the same memory used by the b.config(comman d=...) can
          be used by the successive update of the command. I am ignorant about
          Tkinter, but to me it seems that no new memory is really needed each
          loop.

          -------------------

          Dennis Lee Bieber:
          [color=blue]
          >you are creating an endless number of anonymous functions; they may not be garbage collected until the loop ends<[/color]

          I don't know, usually the garbage collector is quite efficient :-)

          [color=blue]
          >Would it not be easier to have a single callback, but change the button Label, and have the callback branch based on the current label?<[/color]

          Right, thank you. It's not a perfect solution, but often it's good
          enough.

          [color=blue]
          >No idea -- my only Tkinter program (at work, so I can't test it), doesn't do anything like this.<[/color]

          With more tests and some googling I have found the problem and the
          solution. It seems that the problem was produced by commands like this:
          self.canvas.del ete(self.id)
          Maybe they are called after the Tkinter stop, or window close, or
          something like that.

          To solve the problem I've added this to the main window:
          self.root.proto col('WM_DELETE_ WINDOW', self.quitAll)

          And inside the self.quitAll method I fix something before calling
          self.root.quit( )

          [color=blue]
          >I somehow suspect it is faster than a mass of push/pop and shifts...<[/color]

          Your version is very interesting, thank you. I'll use this modified
          version:

          ! _nibbles = {"0":"0000", "1":"0001", "2":"0010", "3":"0011",
          ! "4":"0100", "5":"0101", "6":"0110", "7":"0111",
          ! "8":"1000", "9":"1001", "A":"1010", "B":"1011",
          ! "C":"1100", "D":"1101", "E":"1110", "F":"1111",
          ! "-":"-"}
          !
          ! def to_base2(number ):
          ! result = [_nibbles[nibble] for nibble in "%X"%number]
          ! return int("".join(res ult))

          This is faster than my version (but if you are using Psyco for all the
          tests, for small numbers my version is faster).
          This version is expecially faster for very long numbers.
          But the difference between the two versions is quite little still, a C
          function like the base2 conversion of GMPY is much faster, so I think
          that such common operation can be added to the % formatting sintax.

          [color=blue]
          >Main problem I see with this is that each call to threading.Timer () is creating a NEW thread just for one tick. Lots of overhead involved. I'd create a single thread whose main run method is a continuous loop, using time.sleep() to suspend between ticks. .Timer() is optimized for a one-time callback. Note that using _nextInterval allows me perform an adjustment for the time taken by the callback function.<[/color]

          Oh, very nice, you are very gentle, thank you. I'll add few suggestion
          by Roggisch, and I think it will be quite good. If I obtain something
          good I'll even think about putting it in the cookbook (with your names
          in it).

          -------------------

          Diez B. Roggisch:
          [color=blue]
          >It certainly is somewhat ugly - but there might be very good reasons to do so.<[/color]

          Okay.

          [color=blue]
          >Instead of several metronomes, use one thread and compute the gresatest common divisor for all scheduled timings. Use that as sleep interval (your "ticks"), and count ticks for each timer. You should introduce a lower thrshold on tick-length, otherwise you can get into trouble.<[/color]

          Okay, I'll follow your suggestions.

          Thank you all and hugs to Dennis Lee Bieber,
          bearophile

          Comment

          • Dennis Lee Bieber

            #6
            Re: Command config, quitting, binary, Timer

            On 4 Sep 2005 08:48:33 -0700, bearophileHUGS@ lycos.com declaimed the
            following in comp.lang.pytho n:

            [color=blue]
            >[color=green]
            > >you are creating an endless number of anonymous functions; they may not be garbage collected until the loop ends<[/color]
            >
            > I don't know, usually the garbage collector is quite efficient :-)
            >[/color]
            Yes, but only when ref-counts go to 0... it may be that this tight
            loop never allowed stuff to go to 0 ref-counts. It definitely never
            returned control, so besides eating memory that way, any events for the
            GUI framework were also not being handled and had to be queued.[color=blue]
            >
            > Your version is very interesting, thank you. I'll use this modified
            > version:
            >
            > ! _nibbles = {"0":"0000", "1":"0001", "2":"0010", "3":"0011",
            > ! "4":"0100", "5":"0101", "6":"0110", "7":"0111",
            > ! "8":"1000", "9":"1001", "A":"1010", "B":"1011",
            > ! "C":"1100", "D":"1101", "E":"1110", "F":"1111",
            > ! "-":"-"}
            > !
            > ! def to_base2(number ):
            > ! result = [_nibbles[nibble] for nibble in "%X"%number]
            > ! return int("".join(res ult))
            >[/color]
            I'm still concerned about your use of that int() call... What is the
            real use case for this conversion?

            You take in a number -- which, for all but a few languages, is
            internally binary (the exceptions being: true REXX -- using character
            strings for everything, COBOL -- traditionally using BCD arithmetic
            unless one declared a variable usage as COMPutational, and maybe PL/1).
            You then convert this to a human readable representation of binary
            (using character 1 and 0 for each bit)... So far I understand it. But
            then you feed these string representation of a binary number to a
            function that treats it as a string representation of a DECIMAL number,
            and converts said decimal back to an internal binary format which bears
            no resemblence to the 0/1 bits you started with.

            You can't do arithmetic with the result: Say 2 + 3 were the
            originals...

            2d => 0010b
            3d => 0011b
            +(in native binary)
            5d => 0101b

            but in your method, feeding back through int()

            2d => 0010b => 10d (1010b)
            3d => 0011b => 11d (1011b)
            + +
            5d => 0101b !-> 21d (00010101b)

            If the only purpose is to get a displayable binary value, leave the
            result as a string and don't call int(). Actually, with leading 0s, that
            int() call is probably interpreting the string as OCTAL, rather than
            DECIMAL.
            [color=blue]
            > This is faster than my version (but if you are using Psyco for all the
            > tests, for small numbers my version is faster).[/color]

            For moderate numbers, mine is using the, hopefully quite optimized
            string formatting operations (which should be nearly as fast as doing
            the same in C: sprintf(stringb uff, "%X", n);), and then doing hopefully
            fast dictionary lookups to get the pieces.

            --[color=blue]
            > =============== =============== =============== =============== == <
            > wlfraed@ix.netc om.com | Wulfraed Dennis Lee Bieber KD6MOG <
            > wulfraed@dm.net | Bestiaria Support Staff <
            > =============== =============== =============== =============== == <
            > Home Page: <http://www.dm.net/~wulfraed/> <
            > Overflow Page: <http://wlfraed.home.ne tcom.com/> <[/color]

            Comment

            • bearophileHUGS@lycos.com

              #7
              Re: Command config, quitting, binary, Timer

              Dennis Lee Bieber:
              [color=blue]
              >Yes, but only when ref-counts go to 0... it may be that this tight loop never allowed stuff to go to 0 ref-counts. It definitely never returned control, so besides eating memory that way, any events for the GUI framework were also not being handled and had to be queued.<[/color]

              This memory allocation problem is present even without the loop. In
              this other example if you keep cliking on the Go button, the memory
              used keep growing:

              from Tkinter import *
              def dogo(): b.config(comman d=dogo)
              root = Tk()
              b = Button(root, text="Go", command=dogo)
              b.pack()
              root.mainloop()

              [color=blue]
              >with leading 0s, that int() call is probably interpreting the string as OCTAL, rather than DECIMAL.<[/color]

              This can be fixed with a different dictionary that doesn't contain the
              leading 0s, to be used just for the first (not the "-") nibble.

              [color=blue]
              >What is the real use case for this conversion?<[/color]

              You are right, I'll remove the int conversion.

              Thank you, a bear hug,
              Bearophile

              Comment

              • bearophileHUGS@lycos.com

                #8
                Re: Command config, quitting, binary, Timer

                Bearophile>This can be fixed with a different dictionary that doesn't
                contain the leading 0s,<

                No other dict is necessary:

                ! _nibbles = {"0":"0000", "1":"0001", "2":"0010", "3":"0011",
                ! "4":"0100", "5":"0101", "6":"0110", "7":"0111",
                ! "8":"1000", "9":"1001", "A":"1010", "B":"1011",
                ! "C":"1100", "D":"1101", "E":"1110", "F":"1111",
                ! "-":"-"}
                !
                ! def toBase2(number) :
                ! """toBase2(numb er): given an int/long, converts it
                ! to a string containing the number in base 2."""
                ! # From a suggestion by Dennis Lee Bieber.
                ! if number == 0:
                ! return "0"
                ! result = [_nibbles[nibble] for nibble in "%X"%number]
                ! result[number<0] = result[number<0].lstrip("0")
                ! return "".join(res ult)
                !
                ! for i in xrange(-20, 21):
                ! print toBase2(i)

                Bye,
                Bearophile

                Comment

                • Dennis Lee Bieber

                  #9
                  Re: Command config, quitting, binary, Timer

                  On 6 Sep 2005 06:01:40 -0700, bearophileHUGS@ lycos.com declaimed the
                  following in comp.lang.pytho n:

                  [color=blue]
                  > This memory allocation problem is present even without the loop. In
                  > this other example if you keep cliking on the Go button, the memory
                  > used keep growing:
                  >[/color]
                  No idea then -- I'd hope the .mainloop process wasn't that
                  inefficient to keep references to all historical states <G>
                  --[color=blue]
                  > =============== =============== =============== =============== == <
                  > wlfraed@ix.netc om.com | Wulfraed Dennis Lee Bieber KD6MOG <
                  > wulfraed@dm.net | Bestiaria Support Staff <
                  > =============== =============== =============== =============== == <
                  > Home Page: <http://www.dm.net/~wulfraed/> <
                  > Overflow Page: <http://wlfraed.home.ne tcom.com/> <[/color]

                  Comment

                  Working...