commit 93f783e53bd636bf4d89a0ea7cb3b5a71f58db16 Author: tianzhendong <1203886034@qq.com> Date: Fri Jun 17 11:51:57 2022 +0800 20220617,初版v1.0.0 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c96e9d4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,84 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe +build*/ +build-* +build-*/ +build* +!.vscode +debug/ +debug +release/ +release +cmake-build*/ +.idea/ + diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..20b6aaa --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,53 @@ +cmake_minimum_required(VERSION 3.22) +project(DuplicateFilesCheck) + +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) +set(CMAKE_AUTOUIC ON) + +set(CMAKE_PREFIX_PATH "C:/Qt/Qt5.14.2/5.14.2/mingw73_64") + +find_package(Qt5 COMPONENTS + Core + Gui + Widgets + REQUIRED) + +include_directories(resources) + +add_executable(DuplicateFilesCheck src/main.cpp app_win32.rc src/widget.cpp src/widget.h src/widget.ui src/DuplicateFiles.cpp src/DuplicateFiles.h) +target_link_libraries(DuplicateFilesCheck + Qt5::Core + Qt5::Gui + Qt5::Widgets + ) + +if (WIN32) + set(DEBUG_SUFFIX) + if (MSVC AND CMAKE_BUILD_TYPE MATCHES "Debug") + set(DEBUG_SUFFIX "d") + endif () + set(QT_INSTALL_PATH "${CMAKE_PREFIX_PATH}") + if (NOT EXISTS "${QT_INSTALL_PATH}/bin") + set(QT_INSTALL_PATH "${QT_INSTALL_PATH}/..") + if (NOT EXISTS "${QT_INSTALL_PATH}/bin") + set(QT_INSTALL_PATH "${QT_INSTALL_PATH}/..") + endif () + endif () + if (EXISTS "${QT_INSTALL_PATH}/plugins/platforms/qwindows${DEBUG_SUFFIX}.dll") + add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory + "$/plugins/platforms/") + add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + "${QT_INSTALL_PATH}/plugins/platforms/qwindows${DEBUG_SUFFIX}.dll" + "$/plugins/platforms/") + endif () + foreach (QT_LIB Core Gui Widgets) + add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + "${QT_INSTALL_PATH}/bin/Qt5${QT_LIB}${DEBUG_SUFFIX}.dll" + "$") + endforeach (QT_LIB) +endif () diff --git a/README.md b/README.md new file mode 100644 index 0000000..b0bb339 --- /dev/null +++ b/README.md @@ -0,0 +1,8 @@ +# 重复文件检测工具 +## 环境 +- Clion 2022.1.2 +- qt 5.14.2 +## 功能 +- 单个文件计算md5 +- 文件夹内重复文件检测 + \ No newline at end of file diff --git a/favicon.ico b/favicon.ico new file mode 100644 index 0000000..4f497c5 Binary files /dev/null and b/favicon.ico differ diff --git a/resources/close.png b/resources/close.png new file mode 100644 index 0000000..a8c839d Binary files /dev/null and b/resources/close.png differ diff --git a/resources/fullscreen3.png b/resources/fullscreen3.png new file mode 100644 index 0000000..466b711 Binary files /dev/null and b/resources/fullscreen3.png differ diff --git a/resources/fullscreen4.png b/resources/fullscreen4.png new file mode 100644 index 0000000..401b0a8 Binary files /dev/null and b/resources/fullscreen4.png differ diff --git a/resources/min.png b/resources/min.png new file mode 100644 index 0000000..fbd7577 Binary files /dev/null and b/resources/min.png differ diff --git a/src/DuplicateFiles.cpp b/src/DuplicateFiles.cpp new file mode 100644 index 0000000..8d544a6 --- /dev/null +++ b/src/DuplicateFiles.cpp @@ -0,0 +1,75 @@ +// +// Created by 12038 on 2022/6/16. +// +#include "DuplicateFiles.h" + +DuplicateFiles::DuplicateFiles(QObject *) { + +} + +DuplicateFiles::~DuplicateFiles() { + +} + +void DuplicateFiles::calMd5Slot(const QString &filePath) { + QByteArray md5 = calMd5(filePath); + emit md5Signal(md5); +} + +QByteArray DuplicateFiles::calMd5(const QString &filePath) { + QFile file(filePath); + QByteArray md5 = ""; + if(file.open(QIODevice::ReadOnly)){ + QCryptographicHash hash(QCryptographicHash::Md5); + while(!file.atEnd()){ + QByteArray buff = file.read(100 * 1024 * 1024); + hash.addData(buff); + } + md5 = hash.result().toHex(); + file.close(); + } + return md5; +} + +void DuplicateFiles::getFilesSlot(const QString &filesDirPath) { + //获取文件夹下所有的文件 + QStringList files= this->getFiles(filesDirPath); + //计算md5,并存到map中 + QHash md5Results; + for (int i = 0; i < files.count(); i++){ + QString fileName = files.at(i); + QByteArray fileMd5 = calMd5(fileName); + md5Results[fileMd5].append(fileName); + qDebug() <<"file:" << fileName << "md5:"<< fileMd5; + emit process(i + 1, files.count()); + } + //找出map中value大于1的 + for (QHash::iterator itr = md5Results.begin(); itr != md5Results.end(); itr++){ + if(itr.value().count() > 1){ + qDebug()<< "md5:"< fileInfoList = filesDir.entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot); + for(int i = 0; i < fileInfoList.count(); i++){ + QFileInfo fileInfo = fileInfoList.at(i); + if(fileInfo.isDir()){ + QStringList subFiles= getFiles(fileInfo.absoluteFilePath()); + files.append(subFiles); + } + else{ + QString fileName = fileInfo.absoluteFilePath(); + files.append(fileName); + } + } + return files; +} + diff --git a/src/DuplicateFiles.h b/src/DuplicateFiles.h new file mode 100644 index 0000000..f0e8220 --- /dev/null +++ b/src/DuplicateFiles.h @@ -0,0 +1,40 @@ +// +// Created by 12038 on 2022/6/16. +// + +#ifndef DUPLICATEFILESCHECK_DUPLICATEFILES_H +#define DUPLICATEFILESCHECK_DUPLICATEFILES_H + +#include +#include +#include +#include +#include +#include + +class DuplicateFiles : public QObject{ +Q_OBJECT + +public: + DuplicateFiles(QObject *parent = nullptr); + ~DuplicateFiles(); + +signals: + void md5Signal(const QByteArray &); + void filesSignal(const QStringList &); + void process(const int &, const int &); + void duplicateFilesSignal(const QHash &); + +public slots: + void getFilesSlot(const QString & filesDirPath); + void calMd5Slot(const QString &filePath); + +private: + QByteArray calMd5(const QString &filePath); + QStringList getFiles(const QString &); + QHash duplicateFiles; + +}; + + +#endif //DUPLICATEFILESCHECK_DUPLICATEFILES_H diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..a20479a --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,10 @@ +#include +#include "widget.h" + +int main(int argc, char *argv[]) { + QApplication a(argc, argv); + qRegisterMetaType>("QHash"); + Widget w; + w.show(); + return QApplication::exec(); +} diff --git a/src/widget.cpp b/src/widget.cpp new file mode 100644 index 0000000..cf0455d --- /dev/null +++ b/src/widget.cpp @@ -0,0 +1,178 @@ +// +// Created by 12038 on 2022/6/15. +// + +// You may need to build the project (run Qt uic code generator) to get "ui_Widget.h" resolved + +#include "widget.h" +#include "ui_widget.h" + +Widget::Widget(QWidget *parent) + : QWidget(parent) + , ui(new Ui::Widget) +{ + ui->setupUi(this); + //取消菜单栏 + this->setWindowFlags(Qt::FramelessWindowHint); + + //阴影边框效果 + QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect(); + shadow->setBlurRadius(10); + shadow->setColor(Qt::black); + shadow->setOffset(0); + + ui->shadowWidget->setGraphicsEffect(shadow); + + //父窗口透明 + this->setAttribute(Qt::WA_TranslucentBackground); + + //最大化最小化关闭功能实现 + connect(ui->btnMax, SIGNAL(clicked()), this, SLOT(btnMaxClickedSlot())); + connect(ui->btnMin, SIGNAL(clicked()), this, SLOT(btnMinClickedSlot())); + connect(ui->btnClose, SIGNAL(clicked()), this, SLOT(btnCloseClickedSlot())); + + ui->btnMin->setStyleSheet("border-image: url(../resources/min.png)"); + ui->btnMax->setStyleSheet("border-image: url(../resources/fullscreen3.png)"); + ui->btnClose->setStyleSheet("border-image: url(../resources/close.png)"); + + + duplicateFiles = new DuplicateFiles(); + myThread = new QThread(); + duplicateFiles->moveToThread(myThread); + myThread->start(); + connect(duplicateFiles, SIGNAL(destroyed(QObject *)), + myThread, SLOT(deleteLater())); + + ui->labelTitle->setText("文件一致性检测工具"); + connect(ui->btnSelectFile, SIGNAL(clicked(bool)), this, SLOT(calMd5ofFileSlot())); + connect(ui->btnSelectDir, SIGNAL(clicked(bool)), this, SLOT(selectDirSlot())); + connect(this, SIGNAL(calFileMd5Signal(const QString &)), + duplicateFiles, SLOT(calMd5Slot(const QString &))); + connect(duplicateFiles, SIGNAL(md5Signal(const QByteArray &)), + this, SLOT(showFileMd5Slot(const QByteArray &))); + connect(this, SIGNAL(getFilesSignal(const QString &)), + duplicateFiles, SLOT(getFilesSlot(const QString &))); + connect(duplicateFiles, SIGNAL(filesSignal(const QStringList &)), + this, SLOT(filesSlot(const QStringList &))); + + connect(duplicateFiles, SIGNAL(process(const int &, const int &)), + this, SLOT(processSlot(const int &, const int &))); + connect(duplicateFiles, SIGNAL(duplicateFilesSignal(const QHash &)), + this, SLOT(duplicateFilesSlot(const QHash &))); + connect(ui->listWidget, SIGNAL(currentTextChanged(const QString &)), + this, SLOT(currentTextChangedSlot(const QString &))); +} + +Widget::~Widget() +{ + duplicateFiles->deleteLater(); + myThread->exit(); + myThread->wait(10 * 1000); + delete ui; +} + +void Widget::mousePressEvent(QMouseEvent *event) +{ +// QWidget::mousePressEvent(event); + QPoint mouseStartPoint = event->globalPos(); + QPoint windowLeftTopPoint = this->geometry().topLeft(); + this->mousePosInWindow = mouseStartPoint - windowLeftTopPoint; +} + +void Widget::mouseMoveEvent(QMouseEvent *event) +{ +// QWidget::mouseMoveEvent(event); + if(this->mousePosInWindow == QPoint()) return; + QPoint mousePoint = event->globalPos(); + QPoint windowLeftTopPoint = mousePoint - this->mousePosInWindow; + this->move(windowLeftTopPoint); +} + +void Widget::mouseReleaseEvent(QMouseEvent *) +{ + this->mousePosInWindow = QPoint(); +} + +void Widget::closeEvent(QCloseEvent *event) +{ + QMessageBox::StandardButton button; + button=QMessageBox::question(this,tr("退出程序"),QString(tr("确认退出程序?")),QMessageBox::Yes|QMessageBox::No); + if(button==QMessageBox::No) + { + event->ignore(); // 忽略退出信号,程序继续进行 + } + else if(button==QMessageBox::Yes) + { + event->accept(); // 接受退出信号,程序退出 + } +} + +void Widget::btnMaxClickedSlot() +{ + ui->btnMax->setStyleSheet("border-image: url(../resources/fullscreen4.png)"); + if(this->isMaximized()){ + ui->layoutMain->setMargin(9); + ui->btnMax->setStyleSheet("border-image: url(../resources/fullscreen3.png)"); + this->showNormal(); + } + else{ + ui->layoutMain->setMargin(0); + ui->btnMax->setStyleSheet("border-image: url(../resources/fullscreen4.png)"); + this->showMaximized(); + } +} + +void Widget::btnMinClickedSlot() +{ + this->showMinimized(); +} + +void Widget::btnCloseClickedSlot() +{ + this->close(); +} + +void Widget::calMd5ofFileSlot() { + QString path = QFileDialog::getOpenFileName( + this, "选择文件", + "./", + ""); + emit calFileMd5Signal(path); +} + +void Widget::showFileMd5Slot(const QByteArray & md5) { + ui->leMd5Show->setText(""); + ui->leMd5Show->setText(md5); +} + +void Widget::selectDirSlot() { + ui->progressBar->setValue(0); + QString dirPathUrl = QFileDialog::getExistingDirectory(this, "选择文件夹", "./"); + ui->lineDIrShow->setText(dirPathUrl); + emit getFilesSignal(dirPathUrl); +} + +void Widget::filesSlot(const QStringList &files) { + ui->listWidget_2->clear(); + ui->listWidget_2->addItems(files); +} + +void Widget::processSlot(const int &now, const int &total) { + ui->progressBar->setMaximum(total); + ui->progressBar->setValue(now); +} + +void Widget::duplicateFilesSlot(const QHash &duplicateFiles) { + ui->listWidget->clear(); + this->duplicateResults = duplicateFiles; + for(QHash::const_iterator itr = duplicateFiles.begin(); itr != duplicateFiles.end(); itr++){ + ui->listWidget->addItem(itr.key()); + } +} + +void Widget::currentTextChangedSlot(const QString ¤tText) { + ui->listWidget_2->clear(); + ui->listWidget_2->addItems(this->duplicateResults[currentText.toLocal8Bit()]); +} + + diff --git a/src/widget.h b/src/widget.h new file mode 100644 index 0000000..bcaa9cc --- /dev/null +++ b/src/widget.h @@ -0,0 +1,68 @@ +// +// Created by 12038 on 2022/6/15. +// + +#ifndef DUPLICATEFILESCHECK_WIDGET_H +#define DUPLICATEFILESCHECK_WIDGET_H + +#include +#include +#include +#include +#include +#include "DuplicateFiles.h" +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE +namespace Ui { class Widget; } +QT_END_NAMESPACE + +class Widget : public QWidget +{ +Q_OBJECT + +public: + Widget(QWidget *parent = nullptr); + ~Widget(); + +protected: + void mousePressEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + + void closeEvent(QCloseEvent *event); + +signals: + void calFileMd5Signal(const QString &path); + void getFilesSignal(const QString &dirPath); + +public slots: + void showFileMd5Slot(const QByteArray &); + void filesSlot(const QStringList &); + void processSlot(const int &, const int &); + void duplicateFilesSlot(const QHash &); + void currentTextChangedSlot(const QString &); + +private slots: + void btnMaxClickedSlot(); + void btnMinClickedSlot(); + void btnCloseClickedSlot(); + void calMd5ofFileSlot(); + void selectDirSlot(); + +private: + Ui::Widget *ui; + DuplicateFiles * duplicateFiles; + QThread *myThread; + + QPoint mousePosInWindow = QPoint(); + QHash duplicateResults; + + +}; + + +#endif //DUPLICATEFILESCHECK_WIDGET_H diff --git a/src/widget.ui b/src/widget.ui new file mode 100644 index 0000000..3363358 --- /dev/null +++ b/src/widget.ui @@ -0,0 +1,415 @@ + + + Widget + + + + 0 + 0 + 800 + 600 + + + + Widget + + + + 9 + + + 9 + + + 9 + + + 9 + + + + + #shadowWidget{ + background-color: rgb(255, 255, 255); + border-radius: 5px; + } + + + + + 9 + + + 9 + + + 9 + + + 0 + + + 0 + + + + + 0 + + + 9 + + + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 微软雅黑 + 14 + + + + windowTitle + + + Qt::AlignCenter + + + + + + + + 36 + 36 + + + + + 36 + 36 + + + + + 14 + 50 + false + + + + QPushButton + { + border:none; + } + + QPushButton:hover + { + background-color: rgb(232, 232, 232); + } + QPushButton:pressed + { + background-color: rgb(162, 162, 162); + } + + + + + + + + + + + + + + false + + + + 30 + 36 + + + + + 30 + 36 + + + + + 14 + 50 + false + + + + QPushButton + { + border:none; + } + + QPushButton:hover + { + background-color: rgb(232, 232, 232); + } + QPushButton:pressed + { + background-color: rgb(162, 162, 162); + } + + + + + + + + + + + + + + + 36 + 36 + + + + + 36 + 36 + + + + + 14 + 50 + false + + + + QPushButton + { + border:none; + } + + QPushButton:hover + { + background-color: rgb(232, 232, 232); + } + QPushButton:pressed + { + background-color: rgb(162, 162, 162); + } + + + + + + + + + + + + + + false + + + + 30 + 36 + + + + + 30 + 36 + + + + + 14 + 50 + false + + + + QPushButton + { + border:none; + } + + QPushButton:hover + { + background-color: rgb(232, 232, 232); + } + QPushButton:pressed + { + background-color: rgb(162, 162, 162); + } + + + + + + + + + + + + + + + 36 + 36 + + + + + 36 + 36 + + + + + 14 + 50 + false + + + + QPushButton + { + border:none; + border-top-right-radius: 5px; + } + + QPushButton:hover + { + background-color: rgb(253, 0, 0); + } + QPushButton:pressed + { + background-color: rgb(211, 0, 0); + } + + + + + + + + + + + + + + + + + 0 + 0 + + + + + + + Qt::LeftToRight + + + 文件Md5计算 + + + Qt::AlignCenter + + + + + + 选择文件 + + + + + + + + + + + + + 目录重复文件检测 + + + Qt::AlignCenter + + + + + + + + 选择目录 + + + + + + + + + + + + + + + + + + + + + + 0 + + + + + + + + + + + + + + + + + + +