My Tkinter Threading nightmare

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • half.italian@gmail.com

    #1

    My Tkinter Threading nightmare

    Hi all,

    I don't really understand how to properly use threading in my programs,
    however I have managed to get by so far using them improperly. Once
    again I have come up to what I think is something that won't work
    because of the way the program is set up.

    I have a long running process running underneath a gui in a different
    thread, and a progress bar that updates the status of the operation.
    What I would like to do is use something like :

    self.progressWi ndow.protocol(' WM_DELETE_WINDO W', self.callback)

    and setup the callback to kill the operation and then the window when
    the user clicks on the X of the progress window. I've implemented this
    in my code, but clicking on the X does nothing until I kill the main
    thread (i think), and then my callback is run. I've also had problems
    in the past of the gui not refreshing properly, which leads me to
    believe that both of these are thread related. Any help would be
    immensely appreciated.

    ~Sean DiZazzo

    ~~~~~~~~~~~~~~~ ~~~~

    import sys, os, time

    libdir = "/usr/local/sw/lib"
    sys.path.append (libdir)

    import ProgressBarView , Folder, P

    from Tkinter import *
    from tkFileDialog import askopenfilename s
    from tkMessageBox import showerror, showinfo
    import pexpect, ssh_session

    import P
    import Pmw

    rootWin = Tk()

    class Window(Frame):
    label = None
    initialdir = "/"

    dest = "/tmp"
    folder = None
    logpath = "/tmp/Send.log"
    copyindex = 0
    files = []
    progressWindow = None
    session = None
    password = P.P()
    ssh = ssh_session.ssh _session("root" , "XX.XX.XX.X X",
    password.Decryp t(password.sean ))

    timeout = 30

    def __init__(self, parent=None):
    Frame.__init__( self, parent)
    rootWin.title(" Send to Blah")
    rootWin.resizab le(width=0,heig ht=0)
    self.pack()
    self.progress_a nd_log()
    f = Frame(self)
    openfunc = (lambda self=self, name=None: self.openfolder ())
    Button(f, text="Choose", command=openfun c).pack(side="l eft")
    self.label = Label(f, text=' <--- Choose files', bg="grey", width=40,
    justify='left', anchor='w')
    self.label.pack (side="left", anchor='w', padx=5)
    f.pack(padx=10, pady=10, side='left', fill='both', anchor='w')
    submitfunc = (lambda self=self, name=None: self.submit())
    b = Button(self, text="Send", command=submitf unc)
    b.pack(side="ri ght", padx=5)


    def openfolder(self ):
    self.files = []
    self.files_tupl e = askopenfilename s(parent=rootWi n, initialdir="/")

    for file in self.files_tupl e:
    self.files.appe nd(file)

    self.files.sort ()

    label = "Click button to send files -->"
    self.st.configu re(text_state = 'normal')
    self.st.clear()
    self.st.insert( 'end', "Files to be copied:\n")
    self.st.insert( 'end', "~~~~~~~~~~~~~~ ~~~~~\n")
    for file in self.files:
    self.st.insert( 'end', os.path.split(f ile)[-1] + "\n")
    self.st.insert( 'end', " \n \n")
    self.st.configu re(text_state = 'disabled')
    self.label.conf ig(text='Click to send files --- ', justify='right' ,
    anchor='e')

    def callback(self):
    """Need to get the pid here and kill the running process"""

    def submit(self):
    files = self.files
    dest = Folder.Folder(s elf.dest)

    for file in files:
    if self.ssh.exists (os.path.join(d est.path,
    os.path.split(f ile)[-1])):
    showerror("erro r", "The file '%s' already exists" %
    os.path.join(de st.path, os.path.split(f ile)[-1]))
    return 0

    import threading
    geometry = None

    for file in files:
    self.progressWi ndow = Toplevel(rootWi n)
    self.progressWi ndow.protocol(' WM_DELETE_WINDO W', self.callback)
    self.progressWi ndow.geometry(g eometry)
    self.progressWi ndow.title("Pro gress")
    self.progressWi ndow.resizable( width=0,height= 0)

    self.proglabel = Label(self.prog ressWindow, text='Copying.. .',
    anchor=NW, justify=LEFT, width=30)
    self.proglabel. pack(fill=X, expand=1)
    self.progressBa r =
    ProgressBarView .ProgressBarVie w(self.progress Window)
    self.progressBa r.pack(fill=X)

    threading.Threa d(target=self.t hreadedCopy,arg s=(file, dest)).start()

    #grab the location from the first window
    if geometry is None:
    self.progressBa r.updateProgres s(0)
    geometry = self.progressWi ndow.geometry()

    self.incrementP rogress(self.pr ogressWindow, file, dest)

    self.label.conf ig(text=" <--- Choose files", justify='left',
    anchor='w')
    self.files=[]

    def progress_and_lo g(self):
    self.st = Pmw.ScrolledTex t(rootWin, borderframe = 1,
    usehullsize = 1,
    hull_width = 400,
    hull_height = 150,
    text_wrap='none ',
    #text_font = fixedFont,
    text_padx = 4,
    text_pady = 4,
    )
    self.st.configu re(text_state = 'disabled')

    self.st.pack(pa dx = 5, pady = 5, fill = 'both', expand = 1)


    def threadedCopy(se lf, file, dest):
    self.ssh.scp(fi le, os.path.join(de st.path, os.path.split(f ile)[1]))

    def appendToLog(sel f, src, dest):
    dest_size = self.ssh.size(d est.path)
    try:
    dest_size = float(dest_size )
    except:
    dest_size = 0

    if src.size == dest_size:
    status = "SUCCESS"
    else:
    status = "FAILED"
    entry = "%s - %s %s, %s\n" % (time.ctime(tim e.time()), status,
    src.name, src.readableSiz e)

    f = open(self.logpa th, 'a')
    f.write(entry)
    f.close()

    self.st.configu re(text_state = 'normal')
    self.st.insert( 'end', entry)
    self.st.configu re(text_state = 'disabled')


    def incrementProgre ss(self, window, file, dest):
    import File
    src = File.File(file)
    dest = File.File(os.pa th.join(dest.pa th, os.path.split(f ile)[-1]))
    start = time.time()


    p = 0
    last_dest = 0
    same_count = 0

    while p < 100:
    d_size = self.ssh.size(d est.path)
    try:
    float(d_size)
    except:
    "Couldn't turn %s into a float" % d_size
    continue

    if last_dest == d_size and same_count == 40:
    showerror("erro r", "Error copying %s\nRemoving the partially
    transferred file and continuing..." % dest.path)
    self.ssh.ssh("s udo rm %s" % dest.path)

    if d_size == None:
    d_size = 1000

    self.proglabel. config(text=src .name)

    p = (float(d_size) / float(src.size) ) * 100.0

    d_size = self.ssh.size(d est.path)
    if last_dest == d_size:
    same_count = same_count +1

    print "P:", p
    self.progressBa r.updateProgres s(p)
    self.update_idl etasks()
    time.sleep(0.25 )
    last_dest = d_size

    self.appendToLo g(src, dest)
    window.destroy( )
    return


    if __name__ == "__main__":
    Window().mainlo op()

  • Paul Rubin

    #2
    Re: My Tkinter Threading nightmare

    half.italian@gm ail.com writes:
    and setup the callback to kill the operation and then the window when
    the user clicks on the X of the progress window. I've implemented this
    in my code, but clicking on the X does nothing until I kill the main
    thread (i think), and then my callback is run. I've also had problems
    in the past of the gui not refreshing properly, which leads me to
    believe that both of these are thread related. Any help would be
    immensely appreciated.
    That was more code than I was willing to sit and read, but basically
    the tkinter thread blocks unless there's tkinter events. The usual
    trick for what you're trying to do is to set up a tk.after event to
    run every 20 msec or so. That can check for commands on a queue to
    launch operations, kill windows, or whatever. In your Window class
    you'd have something like (this is pseudocode, I don't remember the
    exact params and args off the top of my head so you'll have to check
    them):

    def __init__(self, ...):
    ...
    self.after(20, self.idle)

    def idle(self):
    # read and execute any commands waiting on the queue
    while True:
    try:
    func, args, kw = self.cmd_queue. get(block=False )
    except QueueEmpty:
    return
    func (*args, **kw)

    cmd_queue is a queue that you set up ahead of time, and you use it to
    send commands into your gui thread from other threads, such as window
    kill. Don't call gui functions directly as tkinter is not
    thread-safe.

    Comment

    • Paul Rubin

      #3
      Re: My Tkinter Threading nightmare

      Paul Rubin <http://phr.cx@NOSPAM.i nvalidwrites:
      def idle(self):
      # read and execute any commands waiting on the queue
      while True:
      try:
      func, args, kw = self.cmd_queue. get(block=False )
      except QueueEmpty:
      return
      func (*args, **kw)
      Whoops, I forgot, you have to set up the after event again at the end
      of this:

      def idle(self):
      # read and execute any commands waiting on the queue
      while True:
      try:
      func, args, kw = self.cmd_queue. get(block=False )
      except QueueEmpty:
      return
      func (*args, **kw)
      self.after(20, self.idle)

      Comment

      • half.italian@gmail.com

        #4
        Re: My Tkinter Threading nightmare


        On Jan 24, 7:35 pm, Paul Rubin <http://phr...@NOSPAM.i nvalidwrote:
        Paul Rubin <http://phr...@NOSPAM.i nvalidwrites:
        def idle(self):
        # read and execute any commands waiting on the queue
        while True:
        try:
        func, args, kw = self.cmd_queue. get(block=False )
        except QueueEmpty:
        return
        func (*args, **kw)Whoops, I forgot, you have to set up the after event again at the end
        of this:
        >
        def idle(self):
        # read and execute any commands waiting on the queue
        while True:
        try:
        func, args, kw = self.cmd_queue. get(block=False )
        except QueueEmpty:
        return
        func (*args, **kw)
        self.after(20, self.idle)
        Thanks Paul. That has made more sense than all of my scrounging
        combined, but I still need to mess around with the idea and code some.
        How can I put something like the window delete into the command queue?
        Isn't window.protocol () just binding the close window button to a
        function?

        I'll look deeper and experiment and try to make sense of it once again
        tomorrow. :)

        ~Sean

        Comment

        • Paul Rubin

          #5
          Re: My Tkinter Threading nightmare

          half.italian@gm ail.com writes:
          try:
          func, args, kw = self.cmd_queue. get(block=False )
          except QueueEmpty:
          return
          func (*args, **kw)
          >
          Thanks Paul. That has made more sense than all of my scrounging
          combined, but I still need to mess around with the idea and code some.
          How can I put something like the window delete into the command queue?
          Isn't window.protocol () just binding the close window button to a
          function?
          self.progressWi ndow.protocol(' WM_DELETE_WINDO W', self.callback)

          becomes

          gui.cmd_queue.p ut(self.progres sWindow.protoco l,
          ('WM_DELETE_WIN DOW', self.callback))

          where gui is the Window object containing your gui.

          I think there are some recipes like this in ASPN, that give more
          detail about how to do this stuff.

          Comment

          • Aahz

            #6
            Re: My Tkinter Threading nightmare

            In article <7xejpjuvwe.fsf @ruckus.brouhah a.com>,
            Paul Rubin <http://phr.cx@NOSPAM.i nvalidwrote:
            >
            self.progressWi ndow.protocol(' WM_DELETE_WINDO W', self.callback)
            >
            >becomes
            >
            gui.cmd_queue.p ut(self.progres sWindow.protoco l,
            ('WM_DELETE_WIN DOW', self.callback))
            >
            >where gui is the Window object containing your gui.
            >
            >I think there are some recipes like this in ASPN, that give more
            >detail about how to do this stuff.
            You can find a simple example at http://pythoncraft.com/ in the threads
            tutorial.
            --
            Aahz (aahz@pythoncra ft.com) <* http://www.pythoncraft.com/

            Help a hearing-impaired person: http://rule6.info/hearing.html

            Comment

            Working...