FolderWatcher
345 строк · 15.4 Кб
1#include "mainwindow.h"
2#include "ui_mainwindow.h"
3
4MainWindow::MainWindow(QWidget *parent)
5: QMainWindow(parent)
6, ui(new Ui::MainWindow)
7{
8// настройка модели работы с директориями
9ui->setupUi(this);
10dir = new QFileSystemModel;
11dir->setRootPath(QDir::currentPath());
12ui->listView->setModel(dir);
13ui->listView->setRootIndex(dir->index(QDir::currentPath()));
14// Отключаем горячие клавиши для listView
15filter = new ShortcutsEventFilter(this);
16ui->listView->installEventFilter(filter);
17
18ui->path_line->setText(QDir::currentPath());
19
20// настройка модели работы с инфой о директориях
21info = new QStandardItemModel;
22ui->tableView->resizeColumnsToContents();
23QHeaderView *header = ui->tableView->horizontalHeader();
24header->setStretchLastSection(true);
25ui->tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
26ui->tableView->setSelectionMode(QAbstractItemView::SingleSelection);
27ui->tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
28ui->tableView->setModel(info);
29
30// Ищем доступные локальные хранилища и добавляем их в комбо бокс
31QStringList storages_paths;
32foreach (const QStorageInfo &storage, QStorageInfo::mountedVolumes()) {
33if (storage.isValid() && storage.isReady()) {
34storages_paths << storage.rootPath();
35}
36}
37ui->storages_box->addItems(storages_paths);
38
39// Объект для вычислений КС, заносим вычисления в отдельный поток
40HashSum *calculator = new HashSum(this);
41calculator->moveToThread(&hash_sum_thread);
42
43// Заносим логгер в отдельный поток
44Logger *logger = new Logger(this);
45logger->moveToThread(&logger_thread);
46
47// Создаем окно загрузки и скрываем его
48loading_window = new LoadingWindow();
49loading_window->hide();
50
51// Коннектим нажатие на кнопки подсчета КС к слоту-отправителю сигнала с данными о выделенных
52// файлах и папках
53connect(ui->actionSHA_256, &QAction::triggered, this, &MainWindow::chooseSHA_256);
54connect(ui->actionSHA_512, &QAction::triggered, this, &MainWindow::chooseSHA_512);
55connect(ui->actionMD5, &QAction::triggered, this, &MainWindow::chooseMD5);
56
57// Коннектим завершение потока с планированием удаления "вычислителя" КС
58connect(&hash_sum_thread, &QThread::finished, calculator, &QObject::deleteLater);
59// Коннектим сигнал с данными о выделенных файлах и папках со слотом вычисления КС
60connect(this, &MainWindow::returnHashSum, calculator, &HashSum::getHashSums);
61// Коннектим сигнал о завершении вычисления КС с внесением полученных данных в таблицу
62connect(calculator, &HashSum::hashSumsReady, this, &MainWindow::handleHashSumCalculations);
63
64// Коннектим завершение потока с планированием удаления логгера
65connect(&logger_thread, &QThread::finished, logger, &QObject::deleteLater);
66// Коннектим сигнал о возникшей ошибке с логом
67connect(calculator, &HashSum::errorOccured, &Logger::logHashSumToFile);
68// Коннектим сигнал о возникшей ошибке с логом
69connect(this, &MainWindow::errorOccured, &Logger::logSizeToFile);
70
71// // Коннектим сигнал о завершении вычисления КС с показом логов
72// connect(calculator, &HashSum::hashSumsReady, this, &MainWindow::showHashSumLogs);
73// // Коннектим сигнал об ошибках со сбором ошибок
74// connect(calculator, &HashSum::errorOccured, this, &MainWindow::handleHashSumErrors);
75
76hash_sum_thread.start();
77
78logger_thread.start();
79
80// Коннектим двойное нажатие по папке/файлу к его открытию
81connect(ui->listView, &QListView::doubleClicked, this, &MainWindow::goDownDir);
82// Коннектим нажатие по кнопке с поднятием на одну папку наверх
83connect(ui->back_button, &QPushButton::clicked, this, &MainWindow::goUpDir);
84// Коннектим нажатие по кнопке с отображением инфы
85connect(ui->main_info_button, &QPushButton::clicked, this, &MainWindow::showMainInfo);
86// Коннектим выбор доступного хранилища в комбо боксе с его открытием модели
87connect(ui->storages_box, &QComboBox::textActivated, this, &MainWindow::goToStorage);
88// Коннектим изменение корневого пути в модели с его отображением в строке
89connect(dir, &QFileSystemModel::rootPathChanged, ui->path_line, &QLineEdit::setText);
90}
91
92void MainWindow::goDownDir(const QModelIndex &index) {
93// Назначаем новый корень для отображения
94ui->listView->setRootIndex(index);
95// Назначаем новый корень для самой модели
96dir->setRootPath(dir->filePath(index));
97}
98
99void MainWindow::goUpDir() {
100// Получаем адрес директории в которой сейчас находимся,
101// затем пермещаемся на директорию (cdUp()) выше и переназначаем корни
102// для модели и отображения
103QDir now_dir(dir->rootPath());
104now_dir.cdUp();
105ui->listView->setRootIndex(dir->index(now_dir.absolutePath()));
106dir->setRootPath(now_dir.absolutePath());
107}
108
109void MainWindow::goToStorage(const QString &storage_path) {
110// Назначаем новый корень для отображения
111ui->listView->setRootIndex(dir->index(storage_path));
112// Назначаем новый корень для самой модели
113dir->setRootPath(storage_path);
114}
115
116void MainWindow::showMainInfo() {
117//
118info->clear();
119
120// установка названий столбцов
121info->setColumnCount(4);
122info->setHorizontalHeaderLabels(QStringList{"name", "date", "type", "size"});
123
124// Получаем индекс текущей директории и количество файлов в директории
125QModelIndex currentDirIndex = dir->index(dir->rootPath());
126int fileCount = dir->rowCount(currentDirIndex);
127
128if (ui->dir_size_box->isChecked()) {
129ui->info_label->setText("Молчать! Идет подсчет размера директорий");
130ui->info_label->setStyleSheet("color: rgb(255, 165, 0)");
131}
132
133// Проходимся по всем файлам и печатаем информацию о них
134for (int i = 0; i < fileCount; i++) {
135QModelIndex fileIndex = dir->index(i, 0, currentDirIndex);
136
137// Получаем тип файла (расширение)
138QString fileType = (dir->isDir(fileIndex)) ? "Folder" : dir->fileInfo(fileIndex).completeSuffix();
139if (fileType == "") fileType = "File";
140
141// добавляем строку с инфой о файле
142QList<QStandardItem *> row;
143row << new QStandardItem(dir->fileName(fileIndex));
144row << new QStandardItem(dir->lastModified(fileIndex).toString("yyyy-MM-dd HH:mm:ss"));
145row << new QStandardItem(fileType);
146if (fileType != "Folder")
147{
148// Делаем читаемый размер
149double file_size = dir->size(fileIndex);
150row << new QStandardItem(getMinimizedFormSize(file_size));
151}
152else
153{
154// Если стоит галочка в чек боксе, считаем еще и размер папок
155if (ui->dir_size_box->isChecked()) {
156// Задержка для постепенной отрисовки строк
157delay(100);
158unsigned long long dir_size = 0;
159// Переделываем слеши в бэкслеши для понятного формата для функции
160std::wstring path_for_fsys = dir->filePath(fileIndex).replace('/', "\\\\").toStdWString();
161
162try {
163getFoldersizeIterative(path_for_fsys, dir_size);
164// Делаем читаемый размер
165double d_dir_size = dir_size;
166row << new QStandardItem(getMinimizedFormSize(d_dir_size));
167} catch(std::exception &e) {
168// Вписываем сообщение об ошибке
169row << new QStandardItem("ОШИБКА");
170emit errorOccured(e, dir->filePath(fileIndex));
171}
172
173}
174// Если галочки нет - в таблице для папок размер не указывается
175else
176row << new QStandardItem("");
177}
178
179info->appendRow(row);
180}
181ui->info_label->setStyleSheet("color: rgb(0, 0, 0)");
182ui->info_label->setText("Информация");
183}
184
185MainWindow::~MainWindow()
186{
187delete dir;
188delete info;
189delete ui;
190delete loading_window;
191delete filter;
192hash_sum_thread.quit();
193hash_sum_thread.wait();
194logger_thread.quit();
195logger_thread.wait();
196}
197
198void MainWindow::on_info_message_triggered() const
199{
200QMessageBox info_box;
201info_box.setWindowTitle("О программе...");
202info_box.setBaseSize(200, 100);
203info_box.setIcon(QMessageBox::Information);
204info_box.setText("<b>Программа FolderWatcher ver. 0.2</b>");
205info_box.setInformativeText("<b>Разработчик ДИПМаксМакс</b>");
206info_box.exec();
207}
208
209void MainWindow::getFoldersizeIterative(std::wstring rootFolder, unsigned long long &f_size) {
210// Функция от пользователя Amit взята на
211// https://stackoverflow.com/questions/15495756/how-can-i-find-the-size-of-all-files-located-inside-a-folder
212// и переписана в итеративный вид
213// Стек для хранения вложенных путей
214QStack<std::wstring> folders;
215folders.push(rootFolder);
216
217while (!folders.empty()) {
218std::wstring current_folder = folders.top();
219folders.pop();
220
221fsys::path folderPath(current_folder);
222if (fsys::exists(folderPath)) {
223fsys::directory_iterator end_itr;
224for (fsys::directory_iterator dirIte(current_folder, fsys::directory_options::skip_permission_denied);
225dirIte != end_itr; ++dirIte) {
226fsys::path filePath(dirIte->path());
227try {
228if (!fsys::is_directory(dirIte->status())) {
229f_size += fsys::file_size(filePath);
230} else {
231folders.push(filePath.wstring());
232}
233} catch (std::exception &e) {
234// записываем в лог ошибки
235emit errorOccured(e, QString::fromStdString(filePath.string()));
236}
237}
238}
239}
240}
241
242void MainWindow::delay(int n) const
243{
244QTime dieTime= QTime::currentTime().addMSecs(n);
245while (QTime::currentTime() < dieTime)
246QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
247}
248
249QString MainWindow::getMinimizedFormSize(double &f_size) {
250QString unit = "bytes";
251QVector<QString> units {"Kbytes", "Mbytes", "Gbytes"};
252int i = 0;
253// Делим пока можем на 1024 и меняем соответсвтенно приставку
254while (f_size > 1024 && i < 2) {
255f_size /= 1024.0;
256unit = units[i++];
257}
258if (i == 0)
259return QString::number(f_size, 'f', 0) + " " + unit;
260return QString::number(f_size, 'f', 2) + " " + unit;
261}
262
263void MainWindow::handleHashSumCalculations(const HashSumRow &vec_rows, const QString &elapsed_time)
264{
265// Готовим таблицу для хэш сумм
266info->clear();
267info->setColumnCount(2);
268info->setHorizontalHeaderLabels(QStringList{"name", "hash sum"});
269
270// Заполняем таблицу данными полученными вычислителем КС
271for (auto item : vec_rows) {
272QList<QStandardItem *> row;
273row << new QStandardItem(item.first);
274row << new QStandardItem(item.second);
275info->appendRow(row);
276}
277
278ui->info_label->setStyleSheet("color: rgb(0, 0, 0)");
279ui->info_label->setText("Информация. Время вычисления " +
280elapsed_time + " c.");
281// скрываем окно
282loading_window->hide();
283}
284
285void MainWindow::calcFileHashSumTriggered(const ALG_ID &hashAlgorithm) {
286// очищаем лог
287hash_sum_log = "";
288// открываем окно
289loading_window->show();
290emit returnHashSum(ui->listView->selectionModel()->selectedIndexes(), dir, hashAlgorithm);
291}
292
293void MainWindow::handleHashSumErrors(const HashSumErrors &error, const QString &file_path) {
294switch (error) {
295case HashSumErrors::MakeHashSumFileError:
296hash_sum_log += file_path + ": Не удалось создать файл контрольных сумм!\n";
297break;
298case HashSumErrors::DeleteHashSumFileError:
299hash_sum_log += file_path + ": Не удалось удалить файл контрольных сумм!\n";
300break;
301case HashSumErrors::GetHashSumError:
302hash_sum_log += file_path + ": Не удалось получить контрольную сумму файла!\n";
303break;
304case HashSumErrors::CreateHashError:
305hash_sum_log += file_path + ": Не удалось создать хэш!\n";
306break;
307case HashSumErrors::ProviderAccessError:
308hash_sum_log += file_path + ": Не удалось получить доступ к криптопровайдеру!\n";
309break;
310case HashSumErrors::OpenFileError:
311hash_sum_log += file_path + ": Не удалось открыть файл для чтения или получить"
312"доступ к файлам папки!\n";
313break;
314default:
315break;
316}
317}
318
319void MainWindow::showHashSumLogs() {
320QMessageBox info_box;
321info_box.setWindowTitle("Логи вычисления контрольных сумм");
322info_box.setBaseSize(200, 100);
323info_box.setIcon(QMessageBox::Information);
324if (hash_sum_log.isEmpty()) info_box.setText("Контрольные суммы успешно посчитаны");
325else info_box.setText(hash_sum_log);
326info_box.exec();
327}
328
329void MainWindow::chooseSHA_256() {
330calcFileHashSumTriggered(CALG_SHA_256);
331}
332
333void MainWindow::chooseSHA_512() {
334calcFileHashSumTriggered(CALG_SHA_512);
335}
336
337void MainWindow::chooseMD5() {
338calcFileHashSumTriggered(CALG_MD5);
339}
340
341void MainWindow::on_show_log_2_triggered()
342{
343// NOTE: открываем файл с логами (не факт что работает, так и не смог протестить)
344QDesktopServices::openUrl(QUrl::fromLocalFile("log.txt"));
345}
346
347