QTreeWidget实现一个打包小工具

不要低估你的能力,不要高估你的毅力

前言

最近因为需要,着手写了个打包工具,功能挺简单的,目前就是基本的文件和文件夹复制。由于要求使用QT进行开发,个人又没有学过,因此完成下面的内容确实花了不少时间。由于网上的资源较少,而且达不到想要的效果,因此自己动手实现了许多功能,代码可能较为冗余(哭),好在功能实现了,多少有了点欣慰之感。

效果展示

首先,先给出几张效果展示图,目前没有任何美化,只是完成了功能,还有很大的改善空间。

1.运行截图为:

2.从上到下,分为三个板块,功能相同,具体演示为:

就是先打开一个文件夹,然后选择复制文件夹目录,选择复制即可。至于remove,可用于复制出错后删除相应文件,而generate按钮用于自动检索打开文件夹是否有批处理文件,有的话就执行。

这个小工具的难点在于文件夹的树形展示和对相应选中文件的操作。在开始制作时,首先是想在网上找找有没有相应的例子,但遗憾的是找到的都是相应用法(只是将项目通过QT展现出来),没有相关操作。QT好像也没有相应的函数,所以根据QT的QTreeWidget中自带的特性,东补西凑地完成了。

具体实现

这里主要采用了QTreeWidget,前期网上很多都是使用QtreeView,但QTreeView好像主要用于展示,对展示后的相关操作比较麻烦,而且效果不太好,于是转向了QTreeWidget。二者主要区别:

  • QTreeView一般和相应的QXXModel合用,形成Model/View结构.

  • QTreeWidget继承自QTreeView ,是封闭了默认Model的QTreeView,其中的元素是QTreeWidgetItem类型,要插入只需将新建QTreeWidgetItem的父类设为指定的QTreeWidget就行(在QTreeWidgetItem的构造函数中指定),要删除直接delete掉QTreeWidgetItem就行

两者的具体区别在此不多说,根据实际需要选择吧,这个小工具QTreeWidget比较适合,下面是实现源码。

实现源码

代码质量一般,其实相关功能的实现有多中方法,这里用的都是比较简单的,这里仅供参考。

在QT中新建一个widgets项目,命名为Robust,更改相应内容如下:

robust.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#ifndef ROBUST_H
#define ROBUST_H

#include "subqtreewidgetitem.h"
#include "externarg.h"
#include <QMainWindow>
#include <QWidget>
#include <QMessageBox>
#include <QString>
#include <QFileSystemModel>
#include <QFileDialog>
#include <QList>
#include <QFileInfoList>
#include <QTreeWidget>
#include <QTreeWidgetItem>
#include <QProcess>
#include <QDebug>

namespace Ui {
class Robust;
}

class Robust : public QMainWindow
{
Q_OBJECT

public:
explicit Robust(QWidget *parent = 0);
QFileInfoList allfile(subQTreeWidgetItem *root,QString path);
~Robust();
void copy(QString openPath, QString selectPath, QTreeWidget * treeWidget, QTreeWidget * toTreeWidget);
bool copyFileToPath(QString sourceDir ,QString toDir, bool coverFileIfExist);
bool copyDirectoryFiles(const QString &fromDir, const QString &toDir, bool coverFileIfExist);
QString treeItemToFullPath(QTreeWidgetItem* treeItem);
void updateTreeWidgetShow(QString openPath, QTreeWidget * treeWidget);
void updateTreeWidgetOpen(QString openPath, QTreeWidget * treeWidget);
void remove(QString sourcePath, QTreeWidget *treeWidget);
bool openFolder(QString openDir, QTreeWidget * treeWidget);
bool generate(QString openPath,QTreeWidget * treeWidget);

private slots:
void on_open_proj_clicked();
void on_copy_button_clicked();
void on_selectPath_clicked();
void on_generate_clicked();
void on_remove_clicked();
void on_open_proj_2_clicked();
void on_copy_2_clicked();
void on_selectPath_2_clicked();
void on_generate_2_clicked();
void on_remove_2_clicked();
void on_open_proj_3_clicked();
void on_copy_3_clicked();
void on_selectPath_3_clicked();
void on_generate_3_clicked();
void on_remove_3_clicked();

private:
Ui::Robust *ui;
QFileSystemModel *info;
};

#endif // ROBUST_H

externarg.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#ifndef EXTERNARG
#define EXTERNARG

#endif // EXTERNARG
#include <QString>
class ExternArg
{
public:
ExternArg();
//打开文件夹路径(不含文件夹名)
static QString OPEN_PATH_1;
//选择安装文件夹路径
static QString SELECT_PATH_1;
//打开文件件路径(包括文件夹名称)
static QString OPEN_DIR_1;

//打开文件夹路径(不含文件夹名)
static QString OPEN_PATH_2;
//选择安装文件夹路径
static QString SELECT_PATH_2;
//打开文件件路径(包括文件夹名称)
static QString OPEN_DIR_2;

//打开文件夹路径(不含文件夹名)
static QString OPEN_PATH_3;
//选择安装文件夹路径
static QString SELECT_PATH_3;
//打开文件件路径(包括文件夹名称)
static QString OPEN_DIR_3;
};

subqtreewidgetitem.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#ifndef SUBQTREEWIDGETITEM
#define SUBQTREEWIDGETITEM

#endif // SUBQTREEWIDGETITEM
#include <QTreeWidgetItem>
#pragma once
class subQTreeWidgetItem : public QTreeWidgetItem
{

public:
QString absPath;
explicit subQTreeWidgetItem(const QStringList &strings, int type = Type);
explicit subQTreeWidgetItem(QTreeWidget *view, int type = Type);
subQTreeWidgetItem(QTreeWidget *view, const QStringList &strings, int type = Type);
};

robust.cpp

这里只留到第一部分的代码,第二三部分类似。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
#include "robust.h"
#include "ui_robust.h"

Robust::Robust(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::Robust)
{
ui->setupUi(this);
}

Robust::~Robust()
{
delete ui;
}


/************************************************ 基本功能的实现 **************************************************************/


//打开文件夹,并以树形目录展示
bool Robust::openFolder(QString openDir, QTreeWidget * treeWidget)
{
bool ok;
if(QFile::exists(openDir))
{
treeWidget->clear();
treeWidget->setHeaderLabel("project view");
treeWidget->setSortingEnabled(true);
// treeWidget->setSelectionMode(QAbstractItemView::SingleSelection);
treeWidget->setSelectionMode(QAbstractItemView::MultiSelection);
subQTreeWidgetItem* root = new subQTreeWidgetItem(treeWidget,QStringList()<<openDir);
//设置一级目录展开
QStringList list = openDir.split("/");
int len = list.length();
root->setExpanded(true);
root->setFirstColumnSpanned(true);
root->setText(0,list.at(len-1));
root->setIcon(0, QIcon(":/new/src/sourceFile/ico/proj.png"));
root->setSelected(true);
root->setCheckState(1, Qt::Checked);
allfile(root,openDir);
ok =true;
return ok;
}
else
{
ok =false;
return ok;
}
}

//递归打开文件夹以树形结构显示的具体实现
QFileInfoList Robust::allfile(subQTreeWidgetItem *root,QString path)
{
QDir dir(path); //遍历各级子目录
QDir dir_file(path); //遍历子目录中所有文件
dir_file.setFilter(QDir::Files | QDir::Hidden | QDir::NoSymLinks); //获取当前所有文件
dir_file.setSorting(QDir::Size | QDir::Reversed);
QFileInfoList list_file = dir_file.entryInfoList();
for (int i = 0; i < list_file.size(); ++i) { //将当前目录中所有文件添加到treewidget中
QFileInfo fileInfo = list_file.at(i);
QString name2=fileInfo.fileName();
subQTreeWidgetItem* child = new subQTreeWidgetItem(QStringList()<<name2);
child->setToolTip(0, path+"/"+name2);
child->absPath = path+"/"+name2;
child->setIcon(0, QIcon(":/new/src/sourceFile/ico/file_blue.png"));
child->setCheckState(1, Qt::Checked);
root->addChild(child);
}
QFileInfoList folder_list = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); //获取当前所有目录
QFileInfoList file_list=dir.entryInfoList(QDir::Files | QDir::Hidden | QDir::NoSymLinks);

for(int i = 0; i != folder_list.size(); i++) //自动递归添加各目录到上一级目录
{
QString namepath = folder_list.at(i).absoluteFilePath(); //获取路径
QFileInfo folderinfo= folder_list.at(i);
QString name=folderinfo.fileName(); //获取目录名
subQTreeWidgetItem* childroot = new subQTreeWidgetItem(QStringList()<<name);
childroot->setToolTip(0, path+"/"+name);
childroot->absPath = path+"/"+name;
childroot->setIcon(0, QIcon(":/new/src/sourceFile/ico/dir.png"));
childroot->setCheckState(1, Qt::Checked);
root->addChild(childroot); //将当前目录添加成path的子项
QFileInfoList child_file_list = allfile(childroot,namepath); //进行递归
file_list.append(child_file_list);
file_list.append(name);
}
return file_list;
}

//打开工程模块的显示更新
void Robust::updateTreeWidgetOpen(QString openPath, QTreeWidget * treeWidget)
{
openFolder(openPath,treeWidget);
}

//递归获取所选文件或目录的相对路径,在这个基础上加上打开文件夹时的路径(没有文件夹名的路径),构成绝对路径。
QString Robust::treeItemToFullPath(QTreeWidgetItem* treeItem)
{
QString fullPath= treeItem->text(0);
while (treeItem->parent() != NULL)
{
fullPath= treeItem->parent()->text(0) + "/" + fullPath;
treeItem = treeItem->parent();
}
return fullPath;
}

//文件和目录拷贝,这是为拷贝设置相应的路径和调用刷新treeWidget的内容显示。
void Robust::copy(QString openPath, QString selectPath, QTreeWidget * treeWidget, QTreeWidget * toTreeWidget)
{
QList<QTreeWidgetItem *> itemList;
QString sourcePath;
itemList = treeWidget->selectedItems();
foreach(QTreeWidgetItem *item, itemList)
{
sourcePath = openPath + treeItemToFullPath(item);
// QString sourcePath= openPath + treeItemToFullPath(treeWidget->selectedItems().first());
QFileInfo *info;
info = new QFileInfo(sourcePath);
QString destPath;
qDebug()<<"the source path:"<<sourcePath;
if (QFile::exists(selectPath))
{
//复制到所选择的目录
destPath = selectPath+"/"+info->fileName();
qDebug()<<"select path"<<destPath;
}
else
{
QMessageBox::information(this,tr("warning"),tr("need select path"));
return;
}
//如果目标路径包含原路径,则跳过,否则将递归进入死循环
if(destPath.contains(sourcePath, Qt::CaseSensitive)){
qDebug()<<"contains";
continue;
}
if(!info->isDir()){
qDebug()<<"destirtion path:"<<destPath;
bool ok = copyFileToPath(sourcePath,destPath,true);
qDebug()<<"file: "<<ok;
}
else
{
qDebug()<<"destirtion path:"<<destPath;
bool ok = copyDirectoryFiles(sourcePath,destPath,true);
qDebug()<<"dir: "<<ok;
}
}
updateTreeWidgetShow(selectPath, toTreeWidget);
}

//拷贝文件的具体实现
bool Robust::copyFileToPath(QString sourceFile ,QString toFile, bool coverFileIfExist)
{
toFile.replace("\\","/");
if (sourceFile == toFile){
return true;
}
if (!QFile::exists(sourceFile)){
return false;
}
QDir *createfile = new QDir;
bool exist = createfile->exists(toFile);
if (exist){
if(coverFileIfExist){
createfile->remove(toFile);
}
}//end if

if(!QFile::copy(sourceFile, toFile))
{
return false;
}
return true;
}

//拷贝文件夹的具体实现
bool Robust::copyDirectoryFiles(const QString &fromDir, const QString &toDir, bool coverFileIfExist)
{
QDir sourceDir(fromDir);
QDir targetDir(toDir);
if(!targetDir.exists()){ //如果目标目录不存在,则进行创建
if(!targetDir.mkdir(targetDir.absolutePath()))
return false;
}

QFileInfoList fileInfoList = sourceDir.entryInfoList();
foreach(QFileInfo fileInfo, fileInfoList){
if(fileInfo.fileName() == "." || fileInfo.fileName() == "..")
continue;

if(fileInfo.isDir()){ //当为目录时,递归的进行copy
if(!copyDirectoryFiles(fileInfo.filePath(),
targetDir.filePath(fileInfo.fileName()),
coverFileIfExist))
return false;
}
else{ //当允许覆盖操作时,将旧文件进行删除操作
if(coverFileIfExist && targetDir.exists(fileInfo.fileName())){
targetDir.remove(fileInfo.fileName());
}

// 进行文件copy
if(!QFile::copy(fileInfo.filePath(),
targetDir.filePath(fileInfo.fileName()))){
return false;
}
}
}
return true;
}

//复制到所在目录的显示更新
void Robust::updateTreeWidgetShow(QString openPath, QTreeWidget * treeWidget)
{
treeWidget->clear();
QString aimPath;
if (QFile::exists(openPath)){
//复制到所选择的目录
aimPath = openPath;
}
else
{
QMessageBox::information(this,tr("warning"),tr("need to select path"));
return;
}
treeWidget->setHeaderLabel("the copy file");
treeWidget->setSelectionMode(QAbstractItemView::SingleSelection);
treeWidget->setSortingEnabled(true);
subQTreeWidgetItem* root1 = new subQTreeWidgetItem(treeWidget,QStringList()<<aimPath);
//设置一级目录展开
root1->setExpanded(true);
root1->setFirstColumnSpanned(true);
QStringList list1 = aimPath.split("/");
int len1 = list1.length();
root1->setText(0,list1.at(len1-1));
root1->setIcon(0, QIcon(":/new/src/sourceFile/ico/proj.png"));
root1->setSelected(true);
root1->setCheckState(1, Qt::Checked);
allfile(root1,aimPath);
}

//文件生成(编译)
bool Robust::generate(QString openPath,QTreeWidget * treeWidget)
{
QDir dir(openPath);
dir.setFilter(QDir::Files | QDir::Hidden | QDir::NoSymLinks);
dir.setSorting(QDir::Size | QDir::Reversed);

QFileInfoList list = dir.entryInfoList();
for (int i = 0; i < list.size(); ++i) {
QFileInfo fileInfo = list.at(i);
if(fileInfo.suffix() == "bat")
{
QString filepath;
filepath.append(fileInfo.path());
qDebug()<<filepath;
QProcess p(0);
p.setWorkingDirectory(filepath);//指定进程的工作目录
filepath+="/"+fileInfo.fileName();
qDebug()<<filepath;
QString command = filepath;
p.start(command);
p.waitForFinished();
qDebug()<<"the result of exec bat:"<<QString::fromLocal8Bit(p.readAllStandardError());
}
}
updateTreeWidgetOpen(openPath,treeWidget);
return 1;
}

//删除文件或文件夹
void Robust::remove(QString selectPath, QTreeWidget *treeWidget)
{
int lastIndex = selectPath.lastIndexOf("/")+1;
QString temPath = selectPath.mid(0,lastIndex) + treeItemToFullPath(treeWidget->selectedItems().first());
qDebug()<<"the source path:"<<temPath;
if(temPath == selectPath)
{
QMessageBox::information(this,tr("warning"),tr("can't remove the select dir"));
return;
}
else
{
QFileInfo *info;
info = new QFileInfo(temPath);
if(info->isDir())
{
QDir tem = QDir(temPath);
bool ok = tem.removeRecursively();
qDebug()<<ok;
}
else
{
QFile *tem;
tem = new QFile(temPath);
bool ok = tem->remove();
qDebug()<<ok;
}
updateTreeWidgetShow(selectPath, treeWidget);
}
}


/************************************************ 下面是各个模块的具体调用 **************************************************************/


/******** 第一板块的调用(treeWidget_1和treeWidget_2) ************/

//打开某个文件夹,并通过treeWidget_1显示。
void Robust::on_open_proj_clicked()
{
ExternArg::OPEN_DIR_1 = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(this, tr("view file"), QDir::currentPath()));
QString aimPath = ExternArg::OPEN_DIR_1.replace("\\","/");
qDebug()<<aimPath;
QFileInfo * tem;
tem = new QFileInfo(aimPath);
qDebug()<<"absolute path: "<<tem->absolutePath();
//这里设置全局变量用于下面复制时的路径构造
int i = tem->absolutePath().lastIndexOf("/");
if(i == tem->absolutePath().length()-1)
{
ExternArg::OPEN_PATH_1 = tem->absolutePath();
}
else
{
ExternArg::OPEN_PATH_1 = tem->absolutePath()+"/";
}
qDebug()<<"ExternArg::OPEN_PATH"<<ExternArg::OPEN_PATH_1;
bool ok = openFolder(ExternArg::OPEN_DIR_1, ui->treeWidget_1);
qDebug()<<ok;
}

//拷贝函数的调用
void Robust::on_copy_button_clicked()
{
copy(ExternArg::OPEN_PATH_1,ExternArg::SELECT_PATH_1, ui->treeWidget_1, ui->treeWidget_2);
}

//选择路径
void Robust::on_selectPath_clicked()
{
QString path = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(this, tr("view file"), QDir::currentPath()));
ExternArg::SELECT_PATH_1 = path.replace("\\","/");
}

//编译调用
void Robust::on_generate_clicked()
{
QString path = ExternArg::OPEN_DIR_1;
bool ok = generate(path, ui->treeWidget_1);
qDebug()<<ok;
}

//复制到所在文件夹的文件删除或文件夹删除
void Robust::on_remove_clicked()
{
QString sourcePath = ExternArg::SELECT_PATH_1;
if(!QFile::exists(sourcePath))
{
QMessageBox::information(this,tr("warning"),tr("need select path"));
return;
}
remove(sourcePath,ui->treeWidget_2);
}

externarg.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include "externarg.h"

ExternArg::ExternArg() {}

QString ExternArg::OPEN_PATH_1 = "";
QString ExternArg::SELECT_PATH_1 = "";
QString ExternArg::OPEN_DIR_1 = "";

QString ExternArg::OPEN_PATH_2 = "";
QString ExternArg::SELECT_PATH_2 = "";
QString ExternArg::OPEN_DIR_2 = "";

QString ExternArg::OPEN_PATH_3 = "";
QString ExternArg::SELECT_PATH_3 = "";
QString ExternArg::OPEN_DIR_3 = "";

subqtreewidgetitem.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include "subqtreewidgetitem.h"

subQTreeWidgetItem::subQTreeWidgetItem(const QStringList &strings1, int type1)
:QTreeWidgetItem(strings1,type1)
{
absPath = "";
}
subQTreeWidgetItem::subQTreeWidgetItem(QTreeWidget *view1, int type1)
:QTreeWidgetItem(view1, type1)
{
absPath = "";
}
subQTreeWidgetItem::subQTreeWidgetItem(QTreeWidget *view1, const QStringList &strings1, int type1)
:QTreeWidgetItem(view1, strings1, type1)
{
absPath = "";
}

main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
#include "robust.h"
#include "subqtreewidgetitem.h"
#include <QApplication>

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Robust w;
w.show();

return a.exec();
}

结语

自己真的太懒了,已经好久没有认真写过博客了,这篇写得也很马虎,希望能尽早改正吧。

0%