Pff, hard to tell without real code, but it seems that your problem is that tkinter GUIs usually need their mainloop running to be fully operational.
When you use a heavy callback, tkinter stops event-loop waiting for callback to return, and file copying is heavy and long-taking operation.
So the solution is to detach it from mainloop as possible. The common practice is to spawn copy operation in separate thread. You could do so with _thread or threading, the 2nd seems to be simpler:
def copy_callback(from_, to, lock):
th = threading.Thread(target=copy_thread, args=(from_, to, lock))
th.start()
def copy_thread(from_, to, lockobj):
with lockobj:
shutil.copy(from_, to)
root = tkinter.Tk()
lock = threading.Lock() # this one monitores finish of copy operation
tkinter.Button(root, text='start copy', command=lambda: copy_callback(src, dst, lock)).pack()
root.mainloop()
Something like this, it doesn't handle copy exceptions (you could add your own logic as needed). And you should check lock's state somewhere else to signal the end of operation to GUI (for example a check callback using tkiter's after method)
def check_callback(inst, lock, waittime):
def check():
if lock.acquire(blocking=False):
inst['text'] = 'Success!'
lock.release()
else:
inst.after(waittime, func=check)
return check
l = tkinter.Label(root, text='Copying...')
l.pack()
check_callback(l, lock, 100)()