Copying Directory and sub directory - c++

Hi im try to copy a directory and all its contents. I thought a good start is to list all the sub directories so i can get an idea and copy the sub structure to my destination folder.
but i need to get the path from the QDir onwards not the path from the root of the machine,
how do i do this so i get sweetassurfwear/folder/folder instaed of /usr/local/websites/sweetassurfwear/folder/folder
here is my code so far
QDir targetDir1("/home/brett/sweetback");
targetDir1.mkdir("sweetassurfwear");
QDir targetDir("/usr/local/websites/sweetassurfwear");
targetDir.setFilter(QDir::NoDotAndDotDot| QDir::Dirs | QDir::Files);
QDirIterator it(targetDir, QDirIterator::Subdirectories);
while (it.hasNext()) {
QFileInfo Info(it.next().relativePath());
QString testName = QString(Info.fileName());
QString testPath = QString(Info.absoluteFilePath());
if(Info.isDir()){
// QString cpd = "/home/brett/sweetback/sweetassurfwear/";
// targetDir.mkdir(cpd+testName);
QString dirname = Info.filePath();
ui.textEdit->append(QString("Copy Files " + dirname));
}
}

The problem is that as I wrote in the comment, you are constructing the file info with the full path as opposed to relative path as you wish to have.
There are two approaches, both presented below, to solve the issue:
1) The work around would be to remove the "prefix" manually by string manipulation.
2) The nicer solution would be to get the relative path out of the absolute and root paths by using the corresponding QDir convenience method.
main.cpp
#include <QDir>
#include <QDirIterator>
#include <QString>
#include <QFileInfo>
#include <QDebug>
int main()
{
QDir targetDir("/usr/local");
targetDir.setFilter(QDir::NoDotAndDotDot| QDir::Dirs | QDir::Files);
QDirIterator it(targetDir, QDirIterator::Subdirectories);
while (it.hasNext()) {
it.next();
QFileInfo Info = it.fileInfo();
QString testName = Info.fileName();
// Work around
// QString testPath = Info.absoluteFilePath().mid(it.path().size()+1);
// Real solution
QString testPath = QString(targetDir.relativeFilePath(Info.absoluteFilePath()));
qDebug() << "TEST:" << testPath;
if(Info.isDir()) {
// QString cpd = "/home/brett/sweetback/sweetassurfwear/";
// targetDir.mkdir(cpd+testName);
QString dirname = Info.filePath();
}
}
return 0;
}
main.pro
TEMPLATE = app
TARGET = main
QT = core
SOURCES += main.cpp
Build and run
qmake && make && ./main
Note that, you do not really need to build a file info based on the path since the QDirIterator instance can return that directly.

Why not just use relativePath method of this class ? Thanks to this method you can get relative path from your path -> link

Related

How to solve the problem with Thread in QT (C++)

I am writing a program in C++ using QT. Task: The user can specify a directory that consists of other folders, and files are located inside these folders. It is necessary to archive the folders with the file and place the archives in another directory. I am working with an additional thread for processing (I created a Worker class, etc.). I did everything according to the example from the QT documentation. The problem is the following: Everything works on my computer and on computers with Windows 10. But on Windows 7 computers, after processing one large folder, the program crashes. Error: The program does not work, code 0xc0000005. What to do? Is this a problem in the code or do I need to install something additionally? Net Framework, Visual C++, etc. updated, but nothing helped. The program code is attached.
P.S. If you don't use an additional thread, then everything works correctly. But an additional thread is needed so that the processing progress can be displayed, that is, how many directories have been processed
#include "renamewidget.h"
#include "ui_renamewidget.h"
#include <private/qzipwriter_p.h>
QString RenameWidget::filePath; //static field
QString RenameWidget::directoryPath; //static field
QString RenameWidget::resultPath; //static field
RenameWidget::RenameWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::RenameWidget)
{
ui->setupUi(this);
filePath = "C:/Users/user/Desktop/1.xlsx";
directoryPath = "C:/Users/user/Desktop/2";
resultPath = "C:/Users/user/Desktop/3";
connect(this, &RenameWidget::doWork, &worker, &Worker::doWork);
connect(&worker, &Worker::workProgress, this, &RenameWidget::setText);
worker.moveToThread(&thread);
thread.start();
}
void RenameWidget::setText(QString message)
{
ui->lableLoad->setText(message); // setProgress
}
void RenameWidget::on_pb_Rename_clicked()
{
emit doWork();
}
void Worker::doWork()
{
emit workProgress("");
QDir dir;
dir.setPath(RenameWidget::getDirectoryPath());
QStringList listDir = dir.entryList(QDir::Dirs | QDir::NoDot | QDir::NoDotDot);
for (int i = 0; i<listDir.size();i++)
{
QString article = QString::number(i);
dir.setPath(RenameWidget::getDirectoryPath() + "/" + listDir.at(i));
QStringList tempstr = dir.entryList(QDir::Dirs | QDir::NoDot | QDir::NoDotDot);
QString temppath = RenameWidget::getDirectoryPath() + "/" + listDir.at(i) + "/" + tempstr .at(0);
QString tempres = RenameWidget::getResultPath() + "/" + "archives" + "/" + article;
dir.mkpath(tempres);
doArchive(temppath + "/", tempres + "/" + article + ".zip");
emit workProgress("Обработано каталогов: " + QString::number(i+1) + "/" + QString::number(listDir.size()));
}
emit workProgress("Готово");
}
void Worker::doArchive(QString path, QString zippath)
{
QZipWriter zip(zippath);
zip.setCompressionPolicy(QZipWriter::AutoCompress);
QDirIterator it(path, QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot, QDirIterator::Subdirectories);
while (it.hasNext())
{
QString file_path = it.next();
if (it.fileInfo().isFile())
{
QFile file(file_path);
if (!file.open(QIODevice::ReadOnly))
continue;
zip.setCreationPermissions(QFile::permissions(file_path));
QByteArray ba = file.readAll();
zip.addFile(file_path.remove(path), ba);
}
}
zip.close();
}

Traversing directories and their sub-directories

I traverse directories and their sub-directories using this piece of code. I do it in order to get path of only one random file from each end-sub-directories and pass to next end-sub-directory. But, in my implementation, it traverses all the files in end-sub-directories.
For example, from /cars/bmw/model1/ directory , it is enough to get just /cars/bmw/model1/f.png path.
QDirIterator it(selectedReferenceFullDirectory, QDir::Files, QDirIterator::Subdirectories);
while(it.hasNext())
{
...
}
My sub-directories are like:
/cars/bmw/model1/h.png
/cars/bmw/model1/f.png
/cars/bmw/model2/q.png
/cars/bmw/model1/hb/a.png
/cars/bmw/model1/sed/y.png
/cars/audi/model2/sed/y.png
...
So, there is no certain number of sub-diretory. Since there are tons of photos inside the directories, while loop takes long time. Do you have any idea to have better performance? Thank in advance
I propose to iterate not over all files, but over sub directories only. For each directory just take a single file (randomly). Here is how I would do it (simplest solution):
QStringList randomFiles(const QString &path)
{
QDirIterator it(path, QDir::AllDirs | QDir::NoDotAndDotDot,
QDirIterator::Subdirectories);
QStringList filePaths;
while (it.hasNext())
{
it.next();
QDir dir(it.filePath());
auto files = dir.entryInfoList(QDir::Files);
if (files.size() > 0)
{
// Take the first file from each directory.
// This might be a random file too, though.
filePaths.append(files.at(0).absoluteFilePath());
}
}
return filePaths;
}

Getting full path from QListWidget

I have 2 listwidgets, lets call them listwidgetinput and listwidgetoutput. I have alot of files(only file name) on listwidgetinput. And i trim the file name before adding it to listwidgetinput like this it.fileName(). and i transfer the selected files to listdigetoutput like:
QList <QListWidgetItem*> items=ui->listWidgetinput->selectedItems();
for(int j=0;j<items.count();j++)
{
list= items.at(j)->text();
ui->listWidgetOutput->insertItem(j,list);
After i transfer the file can i get the path for all the files?. If Yes, how?
edit: code where whole path is available.
QString Dir, Type;
QStringList Files;
Qlistwidget wid
if (index==0)
{
Dir.append(C:\desktop....);
type.append(".txt")
wid = ui->listwidgetinput_txt;
}
if (index ==1)
{
Dir.append(C:\desktop....);
type.append(".doc")
wid = ui->listwidgetinput_doc
}
QDirIterator it(Dir, QStringList() << Type, QDir::Files, QDirIterator::Subdirectories);
while (it.hasNext())
{
it.next();
Files.append(it.fileName());
}
wid->additems(Files);
Use QListWidgetItem::setData() to pass additional "invisible" properties like the full path when creating the item:
auto item = new QListWidgetItem;
item->setText(fileInfo.fileName());
item->setData(Qt::UserRole, fileInfo.absoluteFilePath());
...
Later you can retrieve it via QListWidgetItem::data():
const auto fullPath = item->data(Qt::UserRole).toString();

Qt - copy a file from one directory to another

I am using QT, I am not able to find out how to copy a file from one directory to another? How can I achieve this?
You can use QFile which provides a copy method.
QFile::copy("/path/file", "/path/copy-of-file");
If destination file exist, QFile::copy will not work. The solution is to verify if destination file exist, then delete it:
if (QFile::exists("/path/copy-of-file"))
{
QFile::remove("/path/copy-of-file");
}
QFile::copy("/path/file", "/path/copy-of-file");
The following code works in windows for specified reasons. This will set the path to specified drive and create the folder you created in Under UI Mode. Then copies the file from source to destination. Here the source is installation directory contained some files which are used for plotting curves. this file are not modified by users. They just use it.
hence this works as copy from installation directory to specified folder
void MainWindow::on_pushButton_2_clicked()
{
QString str5 = ui->lineEdit->text();
QString src = "."; QString setpath;
QDir dir(src);
if(!dir.exists()){
return;
}
dir.cdUp();
//dir.cdUp();
setpath = "E://";
dir.setPath(setpath);
QString dst_path = str5 + QDir::separator() ;
dir.mkpath(dst_path);
dir.cd(dst_path);
QString filename = "gnu.plt";
QString filename2 = "Load curve.plt";
QString filename3 = "tube temp.plt";
QFile file(filename);
QFile file1(filename2);
QFile file2(filename3);
file.copy(src+QDir::separator()+filename, setpath+QDir::separator()+str5+QDir::separator()+filename);
file1.copy(src+QDir::separator()+filename2, setpath+QDir::separator()+str5+QDir::separator()+filename2);
file2.copy(src+QDir::separator()+filename3, setpath+QDir::separator()+str5+QDir::separator()+filename3);
}

Delete all files in a directory

I need to delete all files in a directory using Qt.
All of the files in the directory will have the extension ".txt".
I don't want to delete the directory itself.
Does anyone know how I can do this? I've looked at QDir but am having no luck.
Bjorns Answer tweeked to not loop forever
QString path = "whatever";
QDir dir(path);
dir.setNameFilters(QStringList() << "*.*");
dir.setFilter(QDir::Files);
foreach(QString dirFile, dir.entryList())
{
dir.remove(dirFile);
}
Ignoring the txt extension filtering... Here's a way to delete everything in the folder, including non-empty sub directories:
In QT5, you can use removeRecursively() on dirs. Unfortunately, that removes the whole directory - rather than just emptying it. Here is basic a function to just clear a directory's contents.
void clearDir( const QString path )
{
QDir dir( path );
dir.setFilter( QDir::NoDotAndDotDot | QDir::Files );
foreach( QString dirItem, dir.entryList() )
dir.remove( dirItem );
dir.setFilter( QDir::NoDotAndDotDot | QDir::Dirs );
foreach( QString dirItem, dir.entryList() )
{
QDir subDir( dir.absoluteFilePath( dirItem ) );
subDir.removeRecursively();
}
}
Alternatively, you could use removeRecursively() on the directory you want to clear (which would remove it altogether). Then, recreate it with the same name after that... The effect would be the same, but with fewer lines of code. This more verbose function, however, provides more potential for detailed exception handling to be added if desired, e.g. detecting access violations on specific files / folders...
Call QDir::entryList(QDir::Files) to get a list of all the files in the directory, and then for each fileName that ends in ".txt" call QDir::remove(fileName) to delete the file.
You started in a good way, look at entryList and of course pass the namefilter you want.
To improve on #user3191791's answer (which removes all files and directories), this answer:
Modernises the code with a range-based for loop
Provides optional error checking
The code:
struct FileOperationResult
{
bool success;
QString errorMessage;
};
FileOperationResult removeDirContents(const QString &dirPath)
{
QDir dir(dirPath);
dir.setFilter(QDir::NoDotAndDotDot | QDir::Files);
const QStringList files = dir.entryList();
for (const QString &fileName : files) {
if (!dir.remove(fileName)) {
const QString failureMessage = QString::fromUtf8(
"Failed to remove file %1 from %2").arg(fileName, dirPath);
return { false, failureMessage };
}
}
dir.setFilter(QDir::NoDotAndDotDot | QDir::Dirs);
const QStringList dirs = dir.entryList();
for (const QString &dirName : dirs) {
QDir subDir(dir.absoluteFilePath(dirName));
if (!subDir.removeRecursively()) {
const QString failureMessage = QString::fromUtf8(
"Failed to recursively remove directory %1 from %2").arg(dirName, dirPath);
return { false, failureMessage };
}
}
return { true, QString() };
}
Usage:
const FileOperationResult removeResult = removeDirContents(path);
if (!removeResult.success)
qWarning() << removeResult.errorMessage;
This is how I would do it:
QString path = "name-of-directory";
QDir dir(path);
dir.setNameFilters(QStringList() << "*.txt");
dir.setFilters(QDir::Files);
while(dir.entryList().size() > 0){
dir.remove(dir.entryList().first());
}
Other variant of rreeves's code:
QDir dir("/path/to/file");
dir.setNameFilters(QStringList() << "*.*");
dir.setFilter(QDir::Files);
for(const QString &dirFile: dir.entryList()) {
dir.remove(dirFile);
}
You can achieve this without using Qt: to do so, opendir, readdir, unlink, and even rmdir will be your friends. It's easy to use, just browse the man pages ;).