I am posting this as informational for folks looking for a way to communicate thread progress back to a tkinter Frame or window. I have seen several approaches detailed in SO and other sites, but none really seemed adequate to me. So here is an approach that displays progress as both a message box update and advancing a Scale Widget. It uses the tkinter variable classes StringVar and DoubleVar rather than trying to use callbacks or continuously poll a queue in the main thread.
Comments are, of course, welcome, but it appears this approach works well.
`
import tkinter as tk
from tkinter import ttk, END, NW, GROOVE
import threading
import queue
import time
class App(tk.Tk):
    def __init__(self):
      tk.Tk.__init__(self)
      self.queue = queue.Queue()
      self.msgCt=0
      self.listbox = tk.Listbox(self, width=20, height=5)
      self.scaleVal=tk.DoubleVar()
      self.progressbar = ttk.Scale(self, orient='horizontal',
                                         length=300,
                                         from_=0.0, to=100.0,
                                         variable=self.scaleVal)
      self.scaleVal.set(0.0)
      self.button = tk.Button(self, text="Start", command=self.spawnthread)
      self.msgBtn = tk.Button(self,text="Set Msg", command=self.sendMessage)
      self.msgTxt=tk.StringVar(self,"Messages Here...")
      self.msgBox=tk.Message(self,textvariable=self.msgTxt,width=200,
                             anchor=NW,relief=GROOVE)
      self.listbox.grid(row=0,column=0,columnspan=2)
      self.msgBox.grid(row=1,column=0,columnspan=2)
      self.progressbar.grid(row=2,column=0,columnspan=2)
      self.button.grid(row=3,column=0)
      self.msgBtn.grid(row=3,column=1)
    def spawnthread(self):
      self.button.config(state="disabled")
      self.listbox.delete(0, END)
      self.thread = ThreadedClient(self.queue,self.msgTxt,self.scaleVal)
      self.thread.start()
      self.periodiccall()
    def sendMessage(self,msg=None):
      if not msg==None:
        self.msgTxt.set(msg)
      else:
        self.msgTxt.set("Message {}".format(self.msgCt))
      self.msgCt+=1
    def periodiccall(self):
        self.checkqueue()
        if self.thread.is_alive():
            self.after(100, self.periodiccall)
        else:
            self.button.config(state="active")
    def checkqueue(self):
        while self.queue.qsize():
            try:
                msg = self.queue.get(0)
                self.listbox.insert('end', msg)
                # self.progressbar.step(25)
            except queue.Empty:
                pass
class ThreadedClient(threading.Thread):
    def __init__(self, qu, mtxt,dvar):
        threading.Thread.__init__(self)
        self.queue = qu
        self.msgTxt=mtxt
        self.scaleVal=dvar
    def run(self):
      self.scaleVal.set(0.0)
      for x in range(1, 10):
          time.sleep(2)
          msg = "Function %s finished..." % x
          self.msgTxt.set(msg)
          self.scaleVal.set(x*10)
          self.queue.put(msg)
if __name__ == "__main__":
    app = App()
    app.mainloop()
`