Backup File Utility in Python [Tkinter application]
Hi readers,
This is a graphic software (tkinter) in Python to back up a file on your computer. A backup folder is created in the current directory of the selected file and then every x minutes a backup of the file is done, respecting the maximum number of files in the backup directory.
import xxhash, time, shutil, os, threading from datetime import datetime from Tkinter import Label, Spinbox, Listbox, Scrollbar, Tk, E, W, S, N, Button, Frame import tkFileDialog from os.path import expanduser class App(Frame): def __init__(self, parent): Frame.__init__(self, parent) self.parent = parent # Class Variables self.directory_selected = '' self.filename_selected = '' self.backup_folder_name = 'backup' self.num_max_files = None self.time_for_backup = None self.full_file_path = '' self.backup_running = False self.hash = None self.initUI() def initUI(self): self.parent.title("Application to Backup a File") self.label_minutes = Label(text='Time for Backup (Minutes): ', height=2) self.spinbox_minutes = Spinbox(from_=1, to=100, width=5) self.spinbox_minutes.delete(0, 'end') self.spinbox_minutes.insert(0, 5) self.label_numfiles = Label(text='Maximum number files in backup directory: ') self.spinbox_numfiles = Spinbox(from_=1, to=100, width=5) self.spinbox_numfiles.delete(0, 'end') self.spinbox_numfiles.insert(0, 10) self.scrollbar_loglist = Scrollbar() self.listbox_loglist = Listbox(yscrollcommand=self.scrollbar_loglist.set, width=70) self.scrollbar_loglist.config(command=self.listbox_loglist.yview) self.button_file_choose = Button(text='Choose a file for backup ...', command=self.click_button_start_backup) self.button_stop_backup = Button(text='Stop Backup', command=self.click_button_stop_backup) self.button_stop_backup['state'] = 'disabled' self.button_exit = Button(text='Exit', command=self.click_button_exit) # Here the UI components are designed using grid layout self.label_minutes.grid(row=0, sticky=E) self.spinbox_minutes.grid(row=0, column=1, sticky=W) self.label_numfiles.grid(row=1, sticky=E) self.spinbox_numfiles.grid(row=1, column=1, sticky=W) self.listbox_loglist.grid(row=2, column=0, sticky=W + E + N + S, columnspan=3) self.scrollbar_loglist.grid(row=2, column=3, sticky=N + S) self.button_file_choose.grid(row=3, column=0) self.button_stop_backup.grid(row=3, column=1) self.button_exit.grid(row=3, column=2) # define options for opening or saving a file self.file_opt = options = {} options['initialdir'] = expanduser("~") options['parent'] = self options['title'] = 'Select a file for backup ...' def get_ask_open_file(self): return tkFileDialog.askopenfilename(**self.file_opt) def get_current_datetime(self): return datetime.strftime(datetime.now(), "%d%m%Y_%H%M%S") def get_current_datetime_formatted(self): return datetime.strftime(datetime.now(), "[%d/%m/%Y %H:%M:%S] - ") def log_action(self, msg): self.listbox_loglist.insert(0, self.get_current_datetime_formatted() + msg) def do_file_backup(self): curr_datetime = self.get_current_datetime() nom_arquivo = self.filename_selected.split('.')[0] + '_' + curr_datetime + '.' + \ self.filename_selected.split('.')[1] shutil.copyfile(self.full_file_path, self.directory_selected + os.sep + self.backup_folder_name + os.sep + nom_arquivo) self.log_action('Backup Done - ' + nom_arquivo) def get_file_hash_md5(self, file): BLOCKSIZE = 65536 hasher = xxhash.xxh32() with open(file, 'rb') as afile: buf = afile.read(BLOCKSIZE) while len(buf) > 0: hasher.update(buf) buf = afile.read(BLOCKSIZE) return hasher.hexdigest() def listdir_fullpath(self, d): return [os.path.join(d, f) for f in os.listdir(d)] def delete_oldest_files(self): num_total_files_in_directory = len( self.listdir_fullpath(self.directory_selected + os.sep + self.backup_folder_name)) num_files_to_be_removed = num_total_files_in_directory - self.num_max_files if num_files_to_be_removed > 0: self.log_action('Removing ' + str(num_files_to_be_removed) + ' old files') for i in range(num_files_to_be_removed): file_to_remove = self.get_oldest_file_from_directory( self.directory_selected + os.sep + self.backup_folder_name) os.remove(file_to_remove) self.log_action('An old file was removed - ' + file_to_remove.split(os.sep)[-1]) def get_oldest_file_from_directory(self, dir): return min(self.listdir_fullpath(dir), key=os.path.getctime) def start_loop_backup_job(self): if not self.backup_running: return self.delete_oldest_files() if self.hash != self.get_file_hash_md5(self.full_file_path): self.do_file_backup() else: self.log_action('The file was not changed since last check') self.hash = self.get_file_hash_md5(self.full_file_path) self.parent.after(self.time_for_backup * 1000 * 60, self.start_loop_backup_job) def click_button_start_backup(self): file_choosed = self.get_ask_open_file() if not os.path.isfile(file_choosed): return self.log_action('Backup has been started') self.backup_running = True self.button_file_choose['state'] = 'disabled' self.button_stop_backup['state'] = 'normal' self.directory_selected = os.path.split(os.path.abspath(file_choosed))[0] self.filename_selected = os.path.split(os.path.abspath(file_choosed))[1] self.full_file_path = self.directory_selected + os.sep + self.filename_selected if not os.path.exists(self.directory_selected + os.sep + self.backup_folder_name): os.mkdir(self.directory_selected + os.sep + self.backup_folder_name) self.time_for_backup = int(self.spinbox_minutes.get()) self.num_max_files = int(self.spinbox_numfiles.get()) self.backup_running = True self.start_loop_backup_job() def click_button_stop_backup(self): self.button_file_choose['state'] = 'normal' self.button_stop_backup['state'] = 'disabled' self.log_action('Backup has been stopped') self.backup_running = False def click_button_exit(self): self.parent.destroy() def main(): root = Tk() root.eval('tk::PlaceWindow %s center' % root.winfo_pathname(root.winfo_id())) app = App(root) root.mainloop() if __name__ == '__main__': main()