import platform from tkinter import ttk from customtkinter import * from CTkMenuBar import * from CTkMessagebox import * from typing import List from app.utils_drive import * class App: drives: List[Drive] = [] thread = None selected = None drive_tree = None wipe_result = {"result": None, "err": ""} def __init__(self): set_default_color_theme("dark-blue") set_appearance_mode("dark") self.OS_TYPE = platform.system() self.root = CTk() self.root.title("Disk Eraser") self.root.geometry("800x300") self.root.minsize(800, 300) self.__load_menus() self.__init_table() self.__update_drives() self.root.mainloop() def __load_menus(self): bg_color = self.root._apply_appearance_mode(ThemeManager.theme["CTkFrame"]["fg_color"]) main_menu = CTkMenuBar(self.root, bg_color=bg_color) help_menu = main_menu.add_cascade("Справка") dropdown1 = CustomDropdownMenu(widget=help_menu) dropdown1.add_option(option="Помощь") dropdown1.add_separator() dropdown1.add_option(option="О программе", command=self.__open_about_window) self.root.config(menu=main_menu) self.bottom_frame = CTkFrame(self.root) self.erase_button = CTkButton( self.bottom_frame, text="Стереть диск", command=self.__erase_drive) self.erase_button.pack(side=RIGHT) update_button = CTkButton( self.bottom_frame, text="Обновить", command=self.__update_drives) update_button.pack(side=RIGHT, padx=5) self.method_box = CTkComboBox(master=self.bottom_frame, values=list(erasing_methods.keys()), state="readonly") self.method_box.pack(side=RIGHT, padx=6) method_label = CTkLabel(master=self.bottom_frame, text="Метод: ") method_label.pack(padx=5, side=RIGHT) self.bottom_frame.pack(side=BOTTOM, fill=BOTH, padx=5, pady=5) def __update_drives(self): self.drive_tree.delete(*self.drive_tree.get_children()) self.__load_drives() for drive in self.drives: self.drive_tree.insert("", END, values=( drive.index, drive.name, drive.disk_type, self.__human_size(drive.capacity), drive.serial_num)) if self.selected is not None: items = self.drive_tree.get_children("") for item in items: if self.drive_tree.item(item)["values"] == self.selected: self.drive_tree.selection_add(item) def __load_drives(self): if self.OS_TYPE == "Windows": import wmi c = wmi.WMI() self.drives = [] if disks := c.Win32_DiskDrive(): for disk in disks: self.drives.append(Drive(disk.Model, disk.Name, disk.InterfaceType, disk.DefaultBlockSize, int( disk.Size), disk.SerialNumber, disk.Index)) elif self.OS_TYPE == "Linux": from diskinfo import DiskInfo di = DiskInfo() disks = di.get_disk_list(sorting=True) self.drives = [] for disk in disks: if "zram" not in disk.get_path(): self.drives.append(Drive(disk.get_model(), disk.get_path(), disk.get_type_str( ), disk.get_logical_block_size(), int(disk.get_size()*512), disk.get_serial_number(), disk.get_device_id())) def __init_table(self): ###Treeview Customisation (theme colors are selected) bg_color = self.root._apply_appearance_mode(ThemeManager.theme["CTkFrame"]["fg_color"]) text_color = self.root._apply_appearance_mode(ThemeManager.theme["CTkLabel"]["text_color"]) selected_color = self.root._apply_appearance_mode(ThemeManager.theme["CTkButton"]["fg_color"]) treestyle = ttk.Style() treestyle.theme_use("clam") treestyle.configure("Treeview", background=bg_color, foreground=text_color, fieldbackground=bg_color, borderwidth=0) treestyle.map('Treeview', background=[('selected', bg_color)], foreground=[('selected', selected_color)]) treestyle.configure("Treeview.Heading", background=bg_color, foreground=text_color, fieldbackground=bg_color, borderwidth=0) treestyle.map('Treeview.Heading', background=[('selected', bg_color)], foreground=[('selected', selected_color)]) self.root.bind("<>", lambda event: self.root.focus_set()) # определяем столбцы columns = ("index", "name", "type", "capacity", "serial_num") self.drive_tree = ttk.Treeview( columns=columns, show="headings", selectmode="browse") self.drive_tree.pack(fill=BOTH, expand=1, side=TOP) # определяем заголовки self.drive_tree.heading("index", text="ID", anchor=W, command=lambda: self.__sort(0, False)) self.drive_tree.heading("name", text="Имя", anchor=W, command=lambda: self.__sort(1, False)) self.drive_tree.heading("type", text="Тип", anchor=W, command=lambda: self.__sort(2, False)) self.drive_tree.heading("capacity", text="Объем", anchor=W, command=lambda: self.__sort(3, False)) self.drive_tree.heading("serial_num", text="Серийный номер", anchor=W, command=lambda: self.__sort(4, False)) self.drive_tree.column("#1", stretch=NO, width=60, minwidth=60) self.drive_tree.column("#2", stretch=YES, width=150, minwidth=120) self.drive_tree.column("#3", stretch=NO, width=100, minwidth=100) self.drive_tree.column("#4", stretch=YES, width=150, minwidth=100) self.drive_tree.column("#5", stretch=YES, width=150, minwidth=100) self.drive_tree.bind("<>", self.__drive_selected) def __human_size(self, size): units = ['Б', 'КБ', 'МБ', 'ГБ', 'ТБ', 'ПБ'] for unit in units: if size < 1024: return f"{size:.1f} {unit}" size /= 1024 def __drive_selected(self, event): for selected_item in self.drive_tree.selection(): item = self.drive_tree.item(selected_item) self.selected_id = selected_item self.selected = item["values"] def __erase_drive(self): method = self.method_box.get() if self.selected is None: self.__show_warning("Выберете носитель информации для стирания!") elif method == "": self.__show_warning("Выберете метод стирания данных!") elif self.__ask_question(): self.erase_button.configure(state='disabled') self.drive_tree.configure(selectmode="none") thread = None self.progressbar = CTkProgressBar(master=self.bottom_frame, orientation="horizontal", width=200) self.progressbar.set(0) self.progressbar.pack(side=LEFT, padx=6, pady=6) self.progress_label = CTkLabel(self.bottom_frame, text="0 %") self.progress_label.pack(side=LEFT, padx=5, pady=5) for drive in self.drives: if drive.index == self.selected[0]: self.thread = drive.erase(self.method_box.get(), progressbar=self.progressbar, progress_label=self.progress_label, result=self.wipe_result) self.__check_thread() def __check_thread(self): if self.thread.is_alive(): self.root.after(1000, lambda: self.__check_thread()) else: self.progress_label.pack_forget() self.progressbar.pack_forget() self.drive_tree.configure(selectmode="browse") self.erase_button.configure(state='active') if self.wipe_result["result"]: CTkMessagebox(title="Успешно", message=f"{self.selected[1]} стёрт!") else: CTkMessagebox(title="Ошибка", message=f"{self.wipe_result['err']}") def __show_warning(self, text): CTkMessagebox(title="Ошибка", message=text) def __sort(self, col, reverse): # получаем все значения столбцов в виде отдельного списка l = [(self.drive_tree.set(k, col), k) for k in self.drive_tree.get_children("")] # сортируем список l.sort(reverse=reverse) # переупорядочиваем значения в отсортированном порядке for index, (_, k) in enumerate(l): self.drive_tree.move(k, "", index) # в следующий раз выполняем сортировку в обратном порядке self.drive_tree.heading(col, command=lambda: self.__sort(col, not reverse)) def __ask_question(self): msg = CTkMessagebox(title="Стереть", message=f"Вы уверены, что хотите стереть {self.selected[1]} с серийным номером {self.selected[4]}?", icon="question", option_1="Отмена", option_2="Нет", option_3="Да") response = msg.get() if response == "Да": return True else: return False def __open_about_window(self): CTkMessagebox(title="О программе", message="Программа для гарантированного стирания машинных носителей информации\n(c) Романько М.И., Россия, 2024г.")