0

I have a Tkinter GUI having 2 entry fields, 2 buttons ( initialization of these not shown in code). There is one more button (initialized in code) which performs the main task of performing change detection on two images. Also there is a progress bar.

Now, when the task of change detection has been completed, I want to display the 4 images(pre, post, aligned, chng) returned by wave.changedetection() in a separate Tkinter window. I want the new window to come only after changedetection() has completed.(wave.py is my own file, not some module)

Unfortunately, if I try to add code to make new window, Tk.Toplevel() ,after the wave.changedetection() call, nothing happens and the main GUI window becomes unresponsive and has to be killed.

There is no way to know when the new created thread (start_thread)completes it's work, so that I can do Tk.Toplevel() there.

How can I do what I require?

class GUI(Tkinter.Tk):
    def __init__(self, parent)
        Tkinter.Tk.__init__(self, parent)
        self.parent  = parent
        self.initialize()

    def initialize(self):
        self.button = Tkinter.Button(text = "Start")
        self.button.bind('<Button-1>', self.OnButtonClick)
        self.button.pack()
        self.int = Tkinter.IntVar()
        self.pgbar = Tkinter.ProgressBar(variable = self.int, mode = determinate)

    def OnButtonClick(self,event):
        #this func has been made since I have more buttons.It may seem redundant here
        self.button['command'] = self.start_thread()
        self.update_idletasks()

     def start_thread(self):
        self.int_var.set(1)
        q = queue.Queue()
        self.secondary_thread = threading.Thread(target = self.change)
        self.secondary_thread.start()
        self.after(50, self.check_queue, q)

     def check_queue(self, q):
         while True:            
            try: 
                x = wave.q.get_nowait()
            except queue.Empty :
                self.after(50,self.check_queue,q)
                break
            else:
                self.int_var.set(x)                
                if x == 6:
                    self.button3['state'] = 'normal'
                    break

    def change(self):
    '''The 6 functions of wave.changedetection() change the value of self.int 
       due to which progress bar progresses.'''
          pre, post, aligned, chng = wave.changedetection(self.entry_1.get(),
                                                    self.entry_2.get())
if __name__ == '__main__':
     gui = GUI(None)
     gui.mainloop()

code to update progress bar taken from here (2nd answer,Honest Abe's answer)

Community
  • 1
  • 1
Nancy
  • 315
  • 1
  • 5
  • 16

1 Answers1

0

You have to be able to differentiate name spaces, i.e. this is in the main window and this is in the Toplevel. I would suggest that you get the Toplevels working first and then decide if you want to add threading or not. The code below is a simple example of creating Toplevels and shows how to place widgets in a specific name space (window in this case). You may or may not want a separate "create a Toplevel" class if there are functions you want to associate with each Toplevel's namespace. Also there are examples on the web on using Tkinter's "after" to update a progressbar. That is a different question so start another thread if you have questions about the progressbar.

try:
    import Tkinter as tk     ## Python 2.x
except ImportError:
    import tkinter as tk     ## Python 3.x

from functools import partial

class OpenToplevels():
    """ open and close additional Toplevels with a button
    """
    def __init__(self):
        self.root = tk.Tk()
        self.button_ctr=0

        ## in the "root" namespace *********************
        but=tk.Button(self.root, text="Open a Toplevel",
                      command=self.open_another)
        but.grid(row=0, column=0)
        tk.Button(self.root, text="Exit Tkinter", bg="red",
                  command=self.root.quit).grid(row=1, column=0, sticky="we")
        self.root.mainloop()

    def close_it(self, id):
        ## destroy the window in this id's namespace ***********
        id.destroy()
##        id.withdraw()
##          id.iconify()

    def open_another(self):
        self.button_ctr += 1
        id = tk.Toplevel(self.root)
        id.title("Toplevel #%d" % (self.button_ctr))

        ## in the "id for this Toplevel" namespace ***********
        tk.Button(id, text="Close Toplevel #%d" % (self.button_ctr),
                  command=partial(self.close_it, id),
                  bg="orange", width=20).grid(row=1, column=0)

Ot=OpenToplevels()