Я пишу программу резервного копирования файлов, похожую на Microsoft Synctoy. Он берет исходную папку и папку назначения, а затем проверяет, находятся ли файлы из источника в месте назначения;если они есть, ничего не делайте, если нет, скопируйте файлы. Это основная идея.
Я использовал библиотеку Tkinter для Python, чтобы написать GUI для программы, принимая ввод от пользователя с помощью виджетов Entry, а затем запуская функцию копирования, когда пользователь нажимает кнопку «копировать». ,Под этим на моем GUI находится индикатор выполнения, который я обновляю каждые 100 мс. Это работает путем нахождения размера уже скопированных файлов и деления его на общий размер всех файлов в предоставленной исходной директории. Это дает мне процентное значение завершения, после чего я устанавливаю значения индикатора выполнения.
Проблема, с которой я столкнулся, заключалась в том, что когда файлы копировались программой, графический интерфейс переходил в состояние отсутствия ответа. Чтобы исправить это, я попытался использовать многопоточность (у меня очень ограниченное понимание того, как это использовать). Проблема, с которой я столкнулся сейчас, заключается в том, что когда я нажимаю кнопку «Копировать», она сбрасывает индикатор выполнения на 0, как и должно, затем создает отдельный поток, который выполняет функцию копирования, а затем запускает функцию обновления индикатора выполнения, которая вызывает себя с помощью Tkinter. после функции, но возвращает ошибку «RecursionError: максимальная глубина рекурсии превышена при вызове объекта Python». Я полагаю, что это связано с тем, что функция обновления индикатора выполнения имеет проблему с методом .after и многопоточностью.
Основной файл
import Copy_file
import tkinter as tk
from tkinter import ttk
from PIL import ImageTk, Image
import threading
# setup window
window = tk.Tk()
window.title("Simple File Backup")
window.resizable(False, False)
window.iconbitmap("Images\\Icon.ico")
class main_window:
def __init__(self, root):
# initialise other program
self.copy = Copy_file.copy_file()
# configure styles
self.s = ttk.Style()
self.s.configure("TButton", font=("Humanst521 BT", 15))
self.s.configure("main.TLabel", font=("Humanst521 BT", 10))
self.s.configure("title.TLabel", font=("Humanst521 BT", 19))
# create frames
self.main_frame = ttk.Frame(root, padding=10)
self.main_frame.grid()
self.top_frame = ttk.Frame(self.main_frame)
self.top_frame.grid(row=0)
self.extra_frame = ttk.Frame(self.main_frame)
self.extra_frame.grid(row=1)
# add logo
self.logo = Image.open("Images\\Icon.png")
self.logo = self.logo.resize((50, 50), Image.ANTIALIAS)
self.logo = ImageTk.PhotoImage(self.logo)
self.show_logo = tk.Label(self.top_frame, image=self.logo, relief="sunken")
self.show_logo.image = self.logo
self.show_logo.grid(row=0, column=1, sticky="e")
# title
ttk.Label(self.top_frame, text="Simple File Backup", style="title.TLabel").grid(row=0, column=0, columnspan=2, sticky="w")
# create main layout
ttk.Label(self.top_frame, text="Source Directory: ", style="main.TLabel").grid(row=1, column=0, pady=5)
self.source_directory = ttk.Entry(self.top_frame, width=30)
self.source_directory.grid(row=1, column=1)
ttk.Label(self.top_frame, text="Destination Directory: ", style="main.TLabel").grid(row=2, column=0)
self.destination_directory = ttk.Entry(self.top_frame, width=30)
self.destination_directory.grid(row=2, column=1)
# copy button
self.copy_button = ttk.Button(self.top_frame, text="Copy", style="TButton", command=lambda:self.start())
self.copy_button.grid(row=3, column=0, columnspan=2, pady=5)
# extra items layout
self.progress_bar = ttk.Progressbar(self.extra_frame, orient="horizontal", length=300, value=0)
self.progress_bar.grid(row=0, column=0, columnspan=2)
self.output_box = tk.Text(self.extra_frame, takefocus=0, width=40, height=3)
self.output_box.grid(row=1, column=0, columnspan=2, pady=5, padx=5)
def start(self):
self.reset_progressbar()
self.update_progressbar()
self.thread = threading.Thread(target=self.copy.main_copy, args=(self.get_contents(self.source_directory), self.get_contents(self.destination_directory)))
self.thread.start()
def reset_progressbar(self):
self.progress_bar["value"] = 0
def update_progressbar(self):
if not self.progress_bar["value"] == 100.0:
self.progress_bar["value"] = self.copy.current_progress()
print(self.copy.current_progress())
window.after(100, self.update_progressbar())
def get_contents(self, object):
self.output = object.get()
return self.output
# run main_window class with root at tkinter window
main_window(window)
# main gui loop
tk.mainloop()
Файл_копии
import os
import shutil
class copy_file():
def __init__(self):
self.file_bytes_copied = 0
self.full_directory_size = 0
def get_directory_size(self, directory):
self.total_size = 0
for self.dir, self.subdir, self.file in os.walk(directory):
for self.x in range(len(self.file)):
self.total_size += os.stat(self.dir + "\\" + self.file[self.x]).st_size
return self.total_size
def current_progress(self):
if self.file_bytes_copied > 0 and self.full_directory_size > 0:
self.progress = (self.file_bytes_copied/self.full_directory_size)*100
self.progress = round(self.progress, 2)
return self.progress
def concatenate_file_modification(self, path):
self.name = os.path.basename(path)
self.name = self.name.split(".")
self.mod_time = str(os.stat(path).st_mtime)
self.mod_time = self.mod_time.replace(".", "-")
self.concatenate = self.name[0]+" "+self.mod_time+"."+self.name[1]
return self.concatenate
def main_copy(self, source, destination):
self.full_directory_size = self.get_directory_size(source)
self.file_bytes_copied = 0.01
for self.root_dir, self.sub_dir, self.file in os.walk(source):
# remove the first part of the source, leaving the relative directory of the files within the source
self.root_dir = self.root_dir[len(source):]
# create current directory variable
self.current_directory = destination+self.root_dir
# iterate through sub-directories
for i in range(len(self.sub_dir)):
# check if directory exists, if not create it
if not os.path.isdir(self.current_directory+"\\"+self.sub_dir[i]):
os.mkdir(self.current_directory+"\\"+self.sub_dir[i])
print(self.current_directory+"\\"+self.sub_dir[i], "wasn't found in destination, created directory")
# iterate through files
for self.i in range(len(self.file)):
# get the file directory to search for
self.file_to_search = self.current_directory+"\\"+self.concatenate_file_modification(source+self.root_dir+"\\"+self.file[self.i])
# check if path exists, if not copy across file
if not os.path.exists(self.file_to_search):
self.test = shutil.copy2((source + self.root_dir + "\\" + self.file[self.i]), self.file_to_search)
print(self.test)
print(self.file_to_search+" wasn't found in destination, created copy")
# if file does exist, print no action taken
elif os.path.exists(self.file_to_search):
print(self.file_to_search+" was found in destination, no action taken")
print("ok")
# update file_bytes_copied to current progress
self.file_bytes_copied += os.stat(source + self.root_dir + "\\" + self.file[self.i]).st_size