198 lines
9.0 KiB
Python
198 lines
9.0 KiB
Python
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] = []
|
||
|
||
selected = None
|
||
|
||
drive_tree = None
|
||
|
||
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, height=10000)
|
||
|
||
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))
|
||
self.selected = None
|
||
|
||
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 Disk, 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()
|
||
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("<<TreeviewSelect>>", 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("<<TreeviewSelect>>", 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 = 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.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]:
|
||
thread = drive.erase(self.method_box.get(), progressbar=self.progressbar, progress_label=self.progress_label)
|
||
self.__check_thread(thread)
|
||
|
||
def __check_thread(self, thread):
|
||
if thread.is_alive():
|
||
self.root.after(1000, lambda: self.__check_thread(thread))
|
||
else:
|
||
self.progress_label.pack_forget()
|
||
self.progressbar.pack_forget()
|
||
self.drive_tree.configure(selectmode="browse")
|
||
self.erase_button.configure(state='active')
|
||
CTkMessagebox(title="Успешно", message=f"{self.selected[1]} стёрт!")
|
||
|
||
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]}?",
|
||
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) Romanko M.I., Russia, 2024") |