571 lines
18 KiB
C++
571 lines
18 KiB
C++
#include <QDir>
|
|
#include <QDebug>
|
|
#include <QHBoxLayout>
|
|
#include <QStyle>
|
|
#include <QFileInfo>
|
|
#include <QTreeWidgetItemIterator>
|
|
#include <QTreeWidget>
|
|
#include <QTreeWidgetItem>
|
|
#include <QDirIterator>
|
|
#include <QMenu>
|
|
#include <QAction>
|
|
#include <QMessageBox>
|
|
#include <QInputDialog>
|
|
|
|
#include "leftwidget.h"
|
|
#include "filemanager.h"
|
|
#include "global.h"
|
|
#include "logger.h"
|
|
|
|
LeftWidget::LeftWidget(QWidget *parent) : QDockWidget(parent)
|
|
{
|
|
setWindowTitle("文件浏览");
|
|
|
|
_treeWidget = new QTreeWidget();
|
|
_treeWidget->setHeaderHidden(true);
|
|
_treeWidget->setSortingEnabled(false);
|
|
|
|
setWidget(_treeWidget);
|
|
|
|
// 初始化文件树
|
|
reloadFileTree();
|
|
|
|
// connections
|
|
connect(_treeWidget, &QTreeWidget::itemDoubleClicked,
|
|
this, &LeftWidget::slotTreeWidgetItemClicked);
|
|
|
|
// 右键菜单
|
|
_contextMenu = new QMenu(_treeWidget);
|
|
_deleteFileAction = new QAction("删除文件", this);
|
|
_createFolderAction = new QAction("新建文件夹", this);
|
|
_contextMenu->addAction(_deleteFileAction);
|
|
_contextMenu->addAction(_createFolderAction);
|
|
|
|
_treeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
|
|
connect(_treeWidget, &QTreeWidget::customContextMenuRequested,
|
|
this, &LeftWidget::slotShowContextMenu);
|
|
|
|
connect(_deleteFileAction, &QAction::triggered,
|
|
this, &LeftWidget::slotDeleteActionTriggered);
|
|
connect(_createFolderAction, &QAction::triggered,
|
|
this, &LeftWidget::slotCreateFolderActionTriggered);
|
|
}
|
|
|
|
void LeftWidget::reloadFileName()
|
|
{
|
|
clearAllChildItems(_sampleDataItem);
|
|
// clearAllChildItems(_baseLineItem);
|
|
clearAllChildItems(_analysisStateItem);
|
|
|
|
initFileName(_sampleDataItem, Global::SampleDataFloder);
|
|
#if 0
|
|
initFileName(_baseLineItem,Global::BaseLineFolder);
|
|
#endif
|
|
|
|
initFileName(_analysisStateItem, Global::AnalysisStateFolder);
|
|
// 更新文件列表
|
|
Global::updateFileList();
|
|
}
|
|
|
|
QString LeftWidget::filePathCheck(const QString fileName, const QString folderPath)
|
|
{
|
|
QString resultFileName = fileName;
|
|
|
|
QDir dir(folderPath);
|
|
QStringList files = dir.entryList(QDir::Files);
|
|
for (const QString &existedFileName : files)
|
|
{
|
|
QFileInfo fileInfo(existedFileName);
|
|
if (fileName == fileInfo.baseName())
|
|
{
|
|
QDateTime currentDateTime = QDateTime::currentDateTime();
|
|
QString formattedTime = currentDateTime.toString("yyyy_MM_dd_HH_mm_ss");
|
|
|
|
resultFileName = fileName + QString("_") + formattedTime;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return resultFileName;
|
|
}
|
|
|
|
void LeftWidget::initData()
|
|
{
|
|
// const QString folderPath = QDir::currentPath()+"/../experiment_data";
|
|
const QString folderPath = Global::SampleDataFloder;
|
|
#if 1
|
|
QDir dir(folderPath);
|
|
if (!dir.exists())
|
|
{
|
|
qWarning() << "文件夹不存在: " << folderPath;
|
|
return;
|
|
}
|
|
|
|
// 遍历文件
|
|
QFileInfoList fileList = dir.entryInfoList(QDir::Files | QDir::NoDotAndDotDot);
|
|
for (const QFileInfo &fileInfo : fileList)
|
|
{
|
|
QFile file(fileInfo.absoluteFilePath());
|
|
if (file.open(QIODevice::ReadOnly | QIODevice::Text))
|
|
{
|
|
QString content = file.readAll();
|
|
qDebug() << "读取文件 " << fileInfo.absoluteFilePath() << " 内容: " << content;
|
|
file.close();
|
|
// 写入文件操作示例:这里简单在文件末尾追加一行内容
|
|
if (file.open(QIODevice::Append | QIODevice::Text))
|
|
{
|
|
file.write("\n这是通过递归写入添加的内容");
|
|
qDebug() << "已向文件 " << fileInfo.absoluteFilePath() << " 写入内容";
|
|
file.close();
|
|
}
|
|
else
|
|
{
|
|
qWarning() << "无法打开文件进行写入: " << fileInfo.absoluteFilePath();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
qWarning() << "无法打开文件进行读取: " << fileInfo.absoluteFilePath();
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
void LeftWidget::slotTreeWidgetItemClicked(QTreeWidgetItem *item, int column)
|
|
{
|
|
qDebug() << "item clicked:" << item->text(0) << column;
|
|
|
|
if (Global::Mode::Analysis != Global::_mode)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// 检查是否是文件项(有用户数据)
|
|
QVariant userData = item->data(0, Qt::UserRole);
|
|
if (userData.isNull() || !userData.isValid())
|
|
{
|
|
qDebug() << "点击的是目录项或无效项";
|
|
return;
|
|
}
|
|
|
|
QString filePath = userData.toString();
|
|
if (filePath.isEmpty())
|
|
{
|
|
qDebug() << "文件路径为空";
|
|
return;
|
|
}
|
|
|
|
// 检查文件是否存在
|
|
QFileInfo fileInfo(filePath);
|
|
if (!fileInfo.exists())
|
|
{
|
|
qWarning() << "文件不存在: " << filePath;
|
|
return;
|
|
}
|
|
|
|
// 发送文件路径信号
|
|
emit sigSendAnalysisFileName(filePath);
|
|
}
|
|
|
|
void LeftWidget::initFileName(QTreeWidgetItem *parentItem, const QString &folderPath)
|
|
{
|
|
QDir dir(folderPath);
|
|
QStringList files = dir.entryList(QDir::Files);
|
|
for (const QString &fileName : files)
|
|
{
|
|
QTreeWidgetItem *subItem = new QTreeWidgetItem();
|
|
subItem->setText(0, fileName);
|
|
subItem->setData(0, Qt::UserRole, QVariant::fromValue(folderPath + "/" + fileName));
|
|
parentItem->addChild(subItem);
|
|
}
|
|
}
|
|
|
|
void LeftWidget::expandAll(QTreeWidgetItem *item)
|
|
{
|
|
item->setExpanded(true);
|
|
for (int i = 0; i < item->childCount(); ++i)
|
|
{
|
|
expandAll(item->child(i));
|
|
}
|
|
}
|
|
|
|
void LeftWidget::clearAllChildItems(QTreeWidgetItem *parentItem)
|
|
{
|
|
int childCount = parentItem->childCount();
|
|
for (int i = 0; i < childCount; ++i)
|
|
{
|
|
QTreeWidgetItem *childItem = parentItem->takeChild(0);
|
|
delete childItem;
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
void LeftWidget::recursiveFolderOperation(const QString& folderPath) {
|
|
QDir dir(folderPath);
|
|
if (!dir.exists()) {
|
|
qWarning() << "文件夹不存在: " << folderPath;
|
|
return;
|
|
}
|
|
|
|
// 遍历文件
|
|
QFileInfoList fileList = dir.entryInfoList(QDir::Files | QDir::NoDotAndDotDot);
|
|
for (const QFileInfo& fileInfo : fileList) {
|
|
QFile file(fileInfo.absoluteFilePath());
|
|
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
|
QString content = file.readAll();
|
|
qDebug() << "读取文件 " << fileInfo.absoluteFilePath() << " 内容: " << content;
|
|
file.close();
|
|
|
|
// 写入文件操作示例:这里简单在文件末尾追加一行内容
|
|
if (file.open(QIODevice::Append | QIODevice::Text)) {
|
|
file.write("\n这是通过递归写入添加的内容");
|
|
qDebug() << "已向文件 " << fileInfo.absoluteFilePath() << " 写入内容";
|
|
file.close();
|
|
} else {
|
|
qWarning() << "无法打开文件进行写入: " << fileInfo.absoluteFilePath();
|
|
}
|
|
} else {
|
|
qWarning() << "无法打开文件进行读取: " << fileInfo.absoluteFilePath();
|
|
}
|
|
}
|
|
|
|
// 递归遍历子文件夹
|
|
QFileInfoList subDirList = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
|
|
for (const QFileInfo& subDirInfo : subDirList) {
|
|
recursiveFolderOperation(subDirInfo.absoluteFilePath());
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void LeftWidget::slotShowContextMenu(const QPoint &pos)
|
|
{
|
|
QTreeWidgetItem *item = _treeWidget->itemAt(pos);
|
|
if (item) {
|
|
// 获取文件路径
|
|
QString filePath = item->data(0, Qt::UserRole).toString();
|
|
QString dirPath = item->data(0, Qt::UserRole + 1).toString();
|
|
|
|
// 如果是文件
|
|
if (!filePath.isEmpty()) {
|
|
_deleteFileActionFilePath = filePath;
|
|
_deleteFileAction->setVisible(true);
|
|
_deleteFileAction->setText(tr("删除文件"));
|
|
}
|
|
// 如果是目录
|
|
else if (!dirPath.isEmpty()) {
|
|
// 暂时不要删除文件件功能
|
|
_deleteFileAction->setVisible(false);
|
|
#if 0
|
|
_deleteFileAction->setVisible(true);
|
|
_deleteFileActionFilePath = dirPath;
|
|
_deleteFileAction->setText(tr("删除文件夹"));
|
|
#endif
|
|
} else {
|
|
_deleteFileAction->setVisible(false);
|
|
}
|
|
|
|
// 只有当点击的是目录时才显示"新建文件夹"选项
|
|
bool isDir = !dirPath.isEmpty();
|
|
_createFolderAction->setVisible(isDir);
|
|
|
|
_contextMenu->exec(_treeWidget->mapToGlobal(pos));
|
|
}
|
|
}
|
|
|
|
void LeftWidget::slotDeleteActionTriggered()
|
|
{
|
|
if (_deleteFileActionFilePath.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
QFileInfo fileInfo(_deleteFileActionFilePath);
|
|
bool isDir = fileInfo.isDir();
|
|
|
|
// 检查文件/目录是否存在
|
|
if (!fileInfo.exists()) {
|
|
QMessageBox::warning(this, tr("警告"), tr("文件/目录不存在!"));
|
|
return;
|
|
}
|
|
|
|
// 确认删除对话框
|
|
int ret = QMessageBox::question(this, tr("确认删除"),
|
|
tr("确定要删除%1\n%2?").arg(isDir ? tr("文件夹及其所有内容") : tr("文件")).arg(_deleteFileActionFilePath),
|
|
QMessageBox::Yes | QMessageBox::No,
|
|
QMessageBox::No);
|
|
|
|
if (ret == QMessageBox::Yes) {
|
|
bool success;
|
|
if (isDir) {
|
|
// 删除目录及其内容
|
|
success = removeDir(_deleteFileActionFilePath);
|
|
} else {
|
|
// 删除文件
|
|
success = QFile::remove(_deleteFileActionFilePath);
|
|
}
|
|
|
|
if (success) {
|
|
// 重新加载文件树
|
|
reloadFileTree();
|
|
} else {
|
|
QMessageBox::critical(this, tr("错误"), tr("删除失败!"));
|
|
}
|
|
}
|
|
|
|
_deleteFileActionFilePath.clear();
|
|
}
|
|
|
|
bool LeftWidget::removeDir(const QString &dirPath)
|
|
{
|
|
bool result = true;
|
|
QDir dir(dirPath);
|
|
|
|
if (dir.exists(dirPath)) {
|
|
Q_FOREACH(QFileInfo info, dir.entryInfoList(QDir::NoDotAndDotDot | QDir::System | QDir::Hidden | QDir::AllDirs | QDir::Files, QDir::DirsFirst)) {
|
|
if (info.isDir()) {
|
|
result = removeDir(info.absoluteFilePath());
|
|
} else {
|
|
result = QFile::remove(info.absoluteFilePath());
|
|
}
|
|
|
|
if (!result) {
|
|
return result;
|
|
}
|
|
}
|
|
result = dir.rmdir(dirPath);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void LeftWidget::slotCreateFolderActionTriggered()
|
|
{
|
|
// 获取当前选中的项目
|
|
QTreeWidgetItem *currentItem = _treeWidget->currentItem();
|
|
if (!currentItem) {
|
|
return;
|
|
}
|
|
|
|
// 获取当前目录路径
|
|
QString dirPath;
|
|
if (currentItem->data(0, Qt::UserRole + 1).toString().isEmpty()) {
|
|
// 如果是文件,获取其父目录
|
|
dirPath = QFileInfo(currentItem->data(0, Qt::UserRole).toString()).absolutePath();
|
|
} else {
|
|
// 如果是目录,直接使用其路径
|
|
dirPath = currentItem->data(0, Qt::UserRole + 1).toString();
|
|
}
|
|
|
|
// 弹出对话框,输入新文件夹名称
|
|
bool ok;
|
|
QString folderName = QInputDialog::getText(this, tr("新建文件夹"),
|
|
tr("请输入文件夹名称:"),
|
|
QLineEdit::Normal,
|
|
tr("新建文件夹"), &ok);
|
|
|
|
if (!ok || folderName.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
// 检查文件夹名称是否有效
|
|
if (folderName.contains(QRegExp("[\\/:*?\"<>|]"))) {
|
|
QMessageBox::warning(this, tr("错误"), tr("文件夹名称包含非法字符!"));
|
|
return;
|
|
}
|
|
|
|
// 创建新文件夹路径
|
|
QString newFolderPath = dirPath + "/" + folderName;
|
|
|
|
// 检查文件夹是否已存在
|
|
if (QDir(newFolderPath).exists()) {
|
|
QMessageBox::warning(this, tr("错误"), tr("文件夹已存在!"));
|
|
return;
|
|
}
|
|
|
|
// 创建文件夹
|
|
if (QDir().mkdir(newFolderPath)) {
|
|
// 保存当前展开状态
|
|
saveExpandedState();
|
|
|
|
// 重新加载文件树
|
|
reloadFileTree();
|
|
|
|
// 恢复展开状态
|
|
restoreExpandedState();
|
|
|
|
// 展开当前目录并选中新创建的文件夹
|
|
expandAndSelectItem(newFolderPath);
|
|
} else {
|
|
QMessageBox::critical(this, tr("错误"), tr("创建文件夹失败!"));
|
|
}
|
|
}
|
|
|
|
void LeftWidget::expandAndSelectItem(const QString &path)
|
|
{
|
|
// 递归展开到指定路径并选中该项
|
|
QTreeWidgetItemIterator it(_treeWidget);
|
|
while (*it) {
|
|
QTreeWidgetItem *item = *it;
|
|
QString itemPath = item->data(0, Qt::UserRole + 1).toString();
|
|
|
|
if (itemPath == path) {
|
|
// 展开所有父节点
|
|
QTreeWidgetItem *parent = item->parent();
|
|
while (parent) {
|
|
parent->setExpanded(true);
|
|
parent = parent->parent();
|
|
}
|
|
|
|
// 选中新创建的文件夹
|
|
_treeWidget->setCurrentItem(item);
|
|
break;
|
|
}
|
|
++it;
|
|
}
|
|
}
|
|
|
|
void LeftWidget::confirmDelete(bool enabled)
|
|
{
|
|
if (enabled)
|
|
{
|
|
if (QFile::remove(_deleteFileActionFilePath))
|
|
{
|
|
// qDebug() << "文件删除成功:" << filePath;
|
|
logde << "delete succ:" << _deleteFileActionFilePath.toStdString();
|
|
}
|
|
else
|
|
{
|
|
// qWarning() << "文件删除失败:" << filePath;
|
|
logde << "delete fail:" << _deleteFileActionFilePath.toStdString();
|
|
}
|
|
// 重新加载文件树
|
|
reloadFileTree();
|
|
}
|
|
}
|
|
|
|
void LeftWidget::reloadFileTree()
|
|
{
|
|
// 保存当前展开状态
|
|
saveExpandedState();
|
|
|
|
// 清空现有的树形结构
|
|
_treeWidget->clear();
|
|
|
|
// 创建根节点
|
|
QTreeWidgetItem *rootItem = new QTreeWidgetItem(_treeWidget);
|
|
rootItem->setText(0, "实验数据");
|
|
rootItem->setData(0, Qt::UserRole, Global::ExperimentDirPath);
|
|
// 保存根目录路径,用于恢复展开状态
|
|
rootItem->setData(0, Qt::UserRole + 1, Global::ExperimentDirPath);
|
|
|
|
// 递归扫描目录并构建树形结构
|
|
buildFileTree(rootItem, Global::ExperimentDirPath);
|
|
|
|
// 展开根节点
|
|
rootItem->setExpanded(true);
|
|
|
|
// 恢复展开状态
|
|
restoreExpandedState();
|
|
|
|
// 更新全局文件列表
|
|
Global::updateFileList();
|
|
}
|
|
|
|
QFileInfoList LeftWidget::scanDirRecursively(const QString &rootPath)
|
|
{
|
|
QFileInfoList result; // 1. 结果容器
|
|
QDirIterator it(rootPath, // 2. 迭代器:从 rootPath 开始
|
|
QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot, // 3. 要哪些条目
|
|
QDirIterator::Subdirectories); // 4. 递归标志
|
|
while (it.hasNext())
|
|
{ // 5. 还有下一条吗?
|
|
it.next(); // 6. 推进到下一个条目
|
|
result.append(it.fileInfo()); // 7. 把当前条目塞进列表
|
|
}
|
|
return result; // 8. 返回
|
|
}
|
|
|
|
void LeftWidget::buildFileTree(QTreeWidgetItem *parentItem, const QString &dirPath)
|
|
{
|
|
QDir dir(dirPath);
|
|
if (!dir.exists()) {
|
|
qWarning() << "目录不存在: " << dirPath;
|
|
return;
|
|
}
|
|
|
|
// 获取所有子目录和文件,按名称排序
|
|
dir.setSorting(QDir::Name | QDir::DirsFirst);
|
|
QFileInfoList list = dir.entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot);
|
|
|
|
for (const QFileInfo &fileInfo : list) {
|
|
QTreeWidgetItem *item = new QTreeWidgetItem(parentItem);
|
|
|
|
if (fileInfo.isDir()) {
|
|
// 设置目录项
|
|
item->setText(0, fileInfo.fileName());
|
|
// 使用系统默认的目录图标
|
|
item->setIcon(0, _treeWidget->style()->standardIcon(QStyle::SP_DirIcon));
|
|
// 保存目录路径,用于恢复展开状态
|
|
item->setData(0, Qt::UserRole + 1, fileInfo.absoluteFilePath());
|
|
|
|
// 递归处理子目录
|
|
buildFileTree(item, fileInfo.absoluteFilePath());
|
|
} else {
|
|
// 设置文件项
|
|
item->setText(0, fileInfo.fileName());
|
|
item->setData(0, Qt::UserRole, fileInfo.absoluteFilePath());
|
|
|
|
// 根据文件类型设置不同的图标
|
|
QString suffix = fileInfo.suffix().toLower();
|
|
if (suffix == "xlsx" || suffix == "xls") {
|
|
// 使用系统默认的文档图标
|
|
item->setIcon(0, _treeWidget->style()->standardIcon(QStyle::SP_FileIcon));
|
|
} else {
|
|
// 使用默认文件图标
|
|
item->setIcon(0, _treeWidget->style()->standardIcon(QStyle::SP_FileIcon));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void LeftWidget::saveExpandedState()
|
|
{
|
|
_expandedPaths.clear();
|
|
|
|
// 递归遍历所有项,保存展开的目录路径
|
|
QTreeWidgetItemIterator it(_treeWidget);
|
|
while (*it) {
|
|
QTreeWidgetItem *item = *it;
|
|
if (item->isExpanded()) {
|
|
QVariant pathData = item->data(0, Qt::UserRole + 1);
|
|
if (!pathData.isNull()) {
|
|
_expandedPaths.append(pathData.toString());
|
|
}
|
|
}
|
|
++it;
|
|
}
|
|
|
|
qDebug() << "保存了" << _expandedPaths.size() << "个展开状态:" << _expandedPaths;
|
|
}
|
|
|
|
void LeftWidget::restoreExpandedState()
|
|
{
|
|
int restoredCount = 0;
|
|
|
|
// 递归遍历所有项,恢复展开状态
|
|
QTreeWidgetItemIterator it(_treeWidget);
|
|
while (*it) {
|
|
QTreeWidgetItem *item = *it;
|
|
QVariant pathData = item->data(0, Qt::UserRole + 1);
|
|
if (!pathData.isNull()) {
|
|
QString path = pathData.toString();
|
|
if (_expandedPaths.contains(path)) {
|
|
item->setExpanded(true);
|
|
restoredCount++;
|
|
}
|
|
}
|
|
++it;
|
|
}
|
|
|
|
qDebug() << "恢复了" << restoredCount << "个展开状态";
|
|
}
|