基于 Qt C++ 的完整文件管理器实现,支持基本的文件及文件夹的操作如复制、粘贴、删除、重命名等。
项目结构
FileManager/ ├── CMakeLists.txt ├── main.cpp ├── include/ │ ├── filemanager.h │ ├── fileoperations.h │ ├── filemodel.h │ └── fileview.h └── src/ ├── filemanager.cpp ├── fileoperations.cpp ├── filemodel.cpp └── fileview.cpp
模块说明
-
FileManager - 主窗口类,负责整体界面布局和模块协调
-
FileModel - 继承自 QFileSystemModel,提供文件系统数据模型
-
FileView - 继承自 QTreeView,负责显示文件系统视图和用户交互
-
FileOperations - 处理所有文件操作逻辑,与模型和视图解耦
1. CMakeLists.txt
cmake_minimum_required(VERSION 3.5)
project(FileManager)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
find_package(Qt5 REQUIRED COMPONENTS Widgets)
include_directories(include)
file(GLOB SOURCES "src/*.cpp")
add_executable(FileManager main.cpp ${SOURCES})
target_link_libraries(FileManager Qt5::Widgets)
2. main.cpp
#include "include/filemanager.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
FileManager fileManager;
fileManager.show();
return app.exec();
}
3. filemanager.h
#ifndef FILEMANAGER_H
#define FILEMANAGER_H
#include <QMainWindow>
#include "FileManager/filemodel.h"
#include "FileManager/fileview.h"
#include "FileManager/fileoperations.h"
class FileManager : public QMainWindow
{
Q_OBJECT
public:
explicit FileManager(QWidget *parent = nullptr);
void createMenuBar();
void createToolBar();
private:
void setupUi();
void setupConnections();
FileModel *model;
FileView *view;
FileOperations *operations;
};
#endif // FILEMANAGER_H
4. filemanager.cpp
#include "FileManager/filemanager.h"
#include <QToolBar>
#include <QMenu>
#include <QMenuBar>
FileManager::FileManager(QWidget *parent)
: QMainWindow(parent),
model(new FileModel(this)),
view(new FileView(this)),
operations(new FileOperations(model, this))
{
setupUi();
setupConnections();
view->setModel(model);
view->setRootIndex(model->index(QDir::homePath()));
}
void FileManager::setupUi()
{
setWindowTitle("File Manager");
resize(800, 600);
setCentralWidget(view);
// 创建菜单栏
createMenuBar();
// 创建工具栏
createToolBar();
}
void FileManager::createMenuBar()
{
QMenuBar *menuBar = this->menuBar();
// 文件菜单
QMenu *fileMenu = menuBar->addMenu("File(&F)");
QAction *newFolderAction = fileMenu->addAction("NewFolder(&N)");
connect(newFolderAction, &QAction::triggered, this, [this]() {
operations->newFolder(view->currentIndex());
});
fileMenu->addSeparator();
QAction *exitAction = fileMenu->addAction("Exit(&X)");
connect(exitAction, &QAction::triggered, this, &QMainWindow::close);
// 编辑菜单
QMenu *editMenu = menuBar->addMenu("Edit(&E)");
QAction *copyAction = editMenu->addAction("Copy(&C)");
connect(copyAction, &QAction::triggered, this, [this]() {
operations->copyFile(view->currentIndex());
});
QAction *cutAction = editMenu->addAction("Cut(&T)");
connect(cutAction, &QAction::triggered, this, [this]() {
operations->cutFile(view->currentIndex());
});
QAction *pasteAction = editMenu->addAction("Paste(&P)");
connect(pasteAction, &QAction::triggered, this, [this]() {
operations->pasteFile(view->currentIndex());
});
QAction *deleteAction = editMenu->addAction("Delete(&D)");
connect(deleteAction, &QAction::triggered, this, [this]() {
operations->deleteFile(view->currentIndex());
});
QAction *renameAction = editMenu->addAction("Renme(&R)");
connect(renameAction, &QAction::triggered, this, [this]() {
operations->renameFile(view->currentIndex());
});
// 查看菜单
QMenu *viewMenu = menuBar->addMenu("View(&V)");
QAction *homeAction = viewMenu->addAction("Home(&H)");
connect(homeAction, &QAction::triggered, this, [this]() {
view->setRootIndex(model->index(QDir::homePath()));
});
QAction *upAction = viewMenu->addAction("Up(&U)");
connect(upAction, &QAction::triggered, this, [this]() {
QModelIndex currentRoot = view->rootIndex();
if (currentRoot.isValid()) {
view->setRootIndex(currentRoot.parent());
}
});
}
void FileManager::createToolBar()
{
QToolBar *toolBar = addToolBar("ToolBar");
// 添加主页按钮
QAction *homeAction = toolBar->addAction(QIcon::fromTheme("go-home"), "Home");
connect(homeAction, &QAction::triggered, this, [this]() {
view->setRootIndex(model->index(QDir::homePath()));
});
// 添加上一级按钮
QAction *upAction = toolBar->addAction(QIcon::fromTheme("go-up"), "Up");
connect(upAction, &QAction::triggered, this, [this]() {
QModelIndex currentRoot = view->rootIndex();
if (currentRoot.isValid()) {
view->setRootIndex(currentRoot.parent());
}
});
toolBar->addSeparator();
// 添加常用操作按钮
QAction *copyAction = toolBar->addAction(QIcon::fromTheme("edit-copy"), "Copy");
connect(copyAction, &QAction::triggered, this, [this]() {
operations->copyFile(view->currentIndex());
});
QAction *cutAction = toolBar->addAction(QIcon::fromTheme("edit-cut"), "Cut");
connect(cutAction, &QAction::triggered, this, [this]() {
operations->cutFile(view->currentIndex());
});
QAction *pasteAction = toolBar->addAction(QIcon::fromTheme("edit-paste"), "Paste");
connect(pasteAction, &QAction::triggered, this, [this]() {
operations->pasteFile(view->currentIndex());
});
QAction *deleteAction = toolBar->addAction(QIcon::fromTheme("edit-delete"), "Del");
connect(deleteAction, &QAction::triggered, this, [this]() {
operations->deleteFile(view->currentIndex());
});
}
void FileManager::setupConnections()
{
connect(view, &FileView::fileOperationRequested, operations, &FileOperations::handleFileOperation);
}
5. filemodel.h
#ifndef FILEMODEL_H
#define FILEMODEL_H
#include <QFileSystemModel>
class FileModel : public QFileSystemModel
{
Q_OBJECT
public:
explicit FileModel(QObject *parent = nullptr);
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
};
#endif // FILEMODEL_H
6. filemodel.cpp
#include "FileManager/filemodel.h"
#include <QIcon>
FileModel::FileModel(QObject *parent)
: QFileSystemModel(parent)
{
setRootPath(QDir::rootPath());
setReadOnly(false);
}
QVariant FileModel::data(const QModelIndex &index, int role) const
{
if (role == Qt::DecorationRole && index.column() == 0) {
QFileInfo info = fileInfo(index);
if (info.isDir()) {
return QIcon::fromTheme("folder");
}
return QIcon::fromTheme("text-x-generic");
}
return QFileSystemModel::data(index, role);
}
7. fileview.h
#ifndef FILEVIEW_H
#define FILEVIEW_H
#include <QTreeView>
class FileView : public QTreeView
{
Q_OBJECT
public:
explicit FileView(QWidget *parent = nullptr);
public slots:
void showContextMenu(const QPoint &pos);
signals:
void fileOperationRequested(const QString &operation, const QModelIndex &index);
protected:
void contextMenuEvent(QContextMenuEvent *event) override;
void mouseDoubleClickEvent(QMouseEvent *event) override;
private:
void setupContextMenu();
};
#endif // FILEVIEW_H
8. fileview.cpp
#include "FileManager/fileview.h"
#include <QMenu>
#include <QContextMenuEvent>
#include <QDesktopServices>
#include <QUrl>
FileView::FileView(QWidget *parent)
: QTreeView(parent)
{
setContextMenuPolicy(Qt::CustomContextMenu);
//setupContextMenu();
//setContextMenuPolicy(Qt::CustomContextMenu);
connect(this, &QTreeView::customContextMenuRequested,
this, &FileView::showContextMenu);
}
void FileView::contextMenuEvent(QContextMenuEvent *event)
{
QModelIndex index = indexAt(event->pos());
if (!index.isValid()) return;
QMenu menu(this);
QAction *openAction = menu.addAction("Open");
QAction *copyAction = menu.addAction("Copy");
QAction *cutAction = menu.addAction("Cut");
QAction *pasteAction = menu.addAction("Paste");
QAction *deleteAction = menu.addAction("Del");
QAction *renameAction = menu.addAction("Rename");
QAction *newFolderAction = menu.addAction("NewFolder");
QAction *selectedAction = menu.exec(event->globalPos());
if (!selectedAction) return;
if (selectedAction == openAction) {
emit fileOperationRequested("open", index);
}
else if (selectedAction == copyAction) {
emit fileOperationRequested("copy", index);
}
else if (selectedAction == cutAction) {
emit fileOperationRequested("cut", index);
}
else if (selectedAction == pasteAction) {
emit fileOperationRequested("paste", index);
}
else if (selectedAction == deleteAction) {
emit fileOperationRequested("delete", index);
}
else if (selectedAction == renameAction) {
emit fileOperationRequested("rename", index);
}
else if (selectedAction == newFolderAction) {
emit fileOperationRequested("newFolder", index);
}
}
void FileView::mouseDoubleClickEvent(QMouseEvent *event)
{
QModelIndex index = indexAt(event->pos());
if (index.isValid()) {
emit fileOperationRequested("open", index);
}
QTreeView::mouseDoubleClickEvent(event);
}
void FileView::setupContextMenu()
{
// 上下文菜单已在contextMenuEvent中动态创建
}
void FileView::showContextMenu(const QPoint &pos)
{
QModelIndex index = indexAt(pos);
QMenu menu(this);
// 创建动作并直接连接信号
QAction *openAction = menu.addAction("open");
connect(openAction, &QAction::triggered, [this, index]() {
emit fileOperationRequested("open", index);
});
QAction *copyAction = menu.addAction("copy");
connect(copyAction, &QAction::triggered, [this, index]() {
emit fileOperationRequested("copy", index);
});
QAction *cutAction = menu.addAction("cut");
connect(cutAction, &QAction::triggered, [this, index]() {
emit fileOperationRequested("cut", index);
});
QAction *pasteAction = menu.addAction("paste");
connect(pasteAction, &QAction::triggered, [this, index]() {
emit fileOperationRequested("paste", index);
});
QAction *deleteAction = menu.addAction("delete");
connect(deleteAction, &QAction::triggered, [this, index]() {
emit fileOperationRequested("delete", index);
});
QAction *renameAction = menu.addAction("rename");
connect(renameAction, &QAction::triggered, [this, index]() {
emit fileOperationRequested("rename", index);
});
QAction *newFolderAction = menu.addAction("newFolder");
connect(newFolderAction, &QAction::triggered, [this, index]() {
emit fileOperationRequested("newFolder", index);
});
// 显示菜单
menu.exec(viewport()->mapToGlobal(pos));
}
9. fileoperations.h
#ifndef FILEOPERATIONS_H
#define FILEOPERATIONS_H
#include <QObject>
#include <QFileSystemModel>
#include <QStringList>
class FileOperations : public QObject
{
Q_OBJECT
public:
explicit FileOperations(QFileSystemModel *model, QObject *parent = nullptr);
public slots:
void handleFileOperation(const QString &operation, const QModelIndex &index);
void newFolder(const QModelIndex &index);
void copyFile(const QModelIndex &index);
void cutFile(const QModelIndex &index);
void pasteFile(const QModelIndex &index);
void deleteFile(const QModelIndex &index);
void renameFile(const QModelIndex &index);
private:
void openFile(const QModelIndex &index);
bool copyDirectory(const QString &srcPath, const QString &dstPath);
QFileSystemModel *model;
QStringList copiedFiles;
enum ActionType { Copy, Cut } currentAction;
};
#endif // FILEOPERATIONS_H
10. fileoperations.cpp
#include "FileManager/fileoperations.h"
#include <QInputDialog>
#include <QMessageBox>
#include <QDir>
#include <QFileInfo>
#include <QDesktopServices>
#include <QUrl>
FileOperations::FileOperations(QFileSystemModel *model, QObject *parent)
: QObject(parent), model(model)
{
}
void FileOperations::handleFileOperation(const QString &operation, const QModelIndex &index)
{
if (operation == "open") {
openFile(index);
}
else if (operation == "copy") {
copyFile(index);
}
else if (operation == "cut") {
cutFile(index);
}
else if (operation == "paste") {
pasteFile(index);
}
else if (operation == "delete") {
deleteFile(index);
}
else if (operation == "rename") {
renameFile(index);
}
else if (operation == "newFolder") {
newFolder(index);
}
}
void FileOperations::openFile(const QModelIndex &index)
{
QString filePath = model->filePath(index);
if (model->isDir(index)) {
emit model->layoutAboutToBeChanged();
emit model->layoutChanged();
}
else {
QDesktopServices::openUrl(QUrl::fromLocalFile(filePath));
}
}
void FileOperations::copyFile(const QModelIndex &index)
{
copiedFiles.clear();
currentAction = Copy;
copiedFiles.append(model->filePath(index));
}
void FileOperations::cutFile(const QModelIndex &index)
{
copiedFiles.clear();
currentAction = Cut;
copiedFiles.append(model->filePath(index));
}
void FileOperations::pasteFile(const QModelIndex &index)
{
if (copiedFiles.isEmpty()) return;
QString destinationPath;
if (index.isValid() && model->isDir(index)) {
destinationPath = model->filePath(index);
}
else {
destinationPath = model->filePath(model->parent(index));
}
foreach(const QString &filePath, copiedFiles) {
QFileInfo fileInfo(filePath);
QString newPath = destinationPath + QDir::separator() + fileInfo.fileName();
if (currentAction == Copy) {
if (fileInfo.isDir()) {
copyDirectory(filePath, newPath);
}
else {
QFile::copy(filePath, newPath);
}
}
else if (currentAction == Cut) {
QFile::rename(filePath, newPath);
}
}
if (currentAction == Cut) {
copiedFiles.clear();
}
}
void FileOperations::deleteFile(const QModelIndex &index)
{
QString filePath = model->filePath(index);
QMessageBox::StandardButton reply;
reply = QMessageBox::question(nullptr, "Confirm Del", "Are u sure delete " + filePath + " ?",
QMessageBox::Yes | QMessageBox::No);
if (reply == QMessageBox::Yes) {
if (model->isDir(index)) {
QDir dir(filePath);
dir.removeRecursively();
}
else {
QFile::remove(filePath);
}
}
}
void FileOperations::renameFile(const QModelIndex &index)
{
QString oldName = model->fileName(index);
bool ok;
QString newName = QInputDialog::getText(nullptr, "Rename", "New Name:", QLineEdit::Normal, oldName, &ok);
if (ok && !newName.isEmpty()) {
QString oldPath = model->filePath(index);
QString newPath = model->filePath(index.parent()) + QDir::separator() + newName;
if (!QFile::rename(oldPath, newPath)) {
QMessageBox::warning(nullptr, "Error", "Rename failed...");
}
}
}
void FileOperations::newFolder(const QModelIndex &index)
{
QString parentPath;
if (index.isValid() && model->isDir(index)) {
parentPath = model->filePath(index);
}
else {
parentPath = model->filePath(model->parent(index));
}
bool ok;
QString folderName = QInputDialog::getText(nullptr, "New Folder", "Folder Name:", QLineEdit::Normal, "", &ok);
if (ok && !folderName.isEmpty()) {
QString newPath = parentPath + QDir::separator() + folderName;
QDir dir;
if (!dir.mkdir(newPath)) {
QMessageBox::warning(nullptr, "Error", "New folder failed..");
}
}
}
bool FileOperations::copyDirectory(const QString &srcPath, const QString &dstPath)
{
QDir dir(srcPath);
if (!dir.exists()) return false;
foreach(const QString &d, dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) {
QString dst = dstPath + QDir::separator() + d;
dir.mkpath(dst);
copyDirectory(srcPath + QDir::separator() + d, dst);
}
foreach(const QString &f, dir.entryList(QDir::Files)) {
QFile::copy(srcPath + QDir::separator() + f, dstPath + QDir::separator() + f);
}
return true;
}
功能说明
这个文件管理器实现了以下功能:
-
基本文件浏览:使用 QFileSystemModel 和 QTreeView 显示文件系统结构
-
文件操作:
-
打开文件/目录
-
复制文件/目录
-
剪切文件/目录
-
粘贴文件/目录
-
删除文件/目录
-
重命名文件/目录
-
新建文件夹
-
-
导航功能:
-
主页按钮:返回用户主目录
-
向上按钮:返回上一级目录
-
-
用户界面:
-
右键上下文菜单
-
工具栏快捷操作
-
确认对话框
-