Gabe Morris
Gabe Morris

Reputation: 872

Button callback only works one time due to threading

I'm only able to use this button's callback once due to how I set up the 'command=' argument. I would like to be able to run this callback function again once it is finished, but I'm at a loss for how I can give the 'command=' argument a new thread object. I press it once and go through the process of the function, but once I press the button again after it is finished, I get 'RuntimeError: threads can only be started once.' Here is the code for the button and the callback:

def ocr_callback():
    no_file_to_save_to = False
    try:
        status_label.pack_forget()
        for f in files:    # files comes from another callback and is globally defined
            name, extension = os.path.splitext(f)
            if extension != '.pdf':
                raise
        if len(files) == 1:
            new_file = filedialog.asksaveasfilename(filetypes=[('PDF', '.pdf')], defaultextension='.pdf')
            if not new_file:
                no_file_to_save_to = True
                raise
            try:
                ocrmypdf.ocr(files[0], new_file, use_threads=True)
            except ocrmypdf.exceptions.PriorOcrFoundError:
                ocrmypdf.ocr(files[0], new_file, redo_ocr=True, use_threads=True)
        elif len(files) > 1:
            directory = filedialog.askdirectory()
            if not directory:
                no_file_to_save_to = True
                raise
            for f in files:
                file_name = f.split('/')[-1]
                try:
                    ocrmypdf.ocr(f, directory + '/' + file_name, use_threads=True)
                except ocrmypdf.exceptions.PriorOcrFoundError:
                    ocrmypdf.ocr(f, directory + '/' + file_name, redo_ocr=True, use_threads=True)
        status_label.config(text='Process Complete!', fg='blue')
        status_label.pack(expand='yes')
    except:
        if no_file_to_save_to:
            status_label.config(text='No file to save to. Process Cancelled', fg='blue')
        else:
            status_label.config(text='Error: One or more of the files could be corrupt.', fg='red')
        status_label.pack(expand='yes')


ocr_button = Button(root, text='OCR Files', relief='groove', bg='#5D1725', bd=0, width=scaled(20), fg='white',
                    command=threading.Thread(target=ocr_callback).start, state=DISABLED)
ocr_button.pack()

Any thoughts for how I could change it to make it work? I know this function must be threaded else the window will stall and freeze itself until it is finished. The 'ocr' function is what causes the bogging and necessity for the threading.

Upvotes: 2

Views: 151

Answers (1)

Reblochon Masque
Reblochon Masque

Reputation: 36682

You should probably start the thread from a launch function, instead of from inside the button command.

Maybe like this:

def launch_cmd(dummy=None):
    threading.Thread(target=ocr_callback).start()

...
ocr_button = Button(root, text='OCR Files', relief='groove', bg='#5D1725',\
                    bd=0, width=scaled(20), fg='white', command=launch_cmd, state=DISABLED)

Upvotes: 1

Related Questions