Check if directory is empty - qt

I'm trying to check if a directory is empty.
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QDir Dir("/home/highlander/Desktop/dir");
if(Dir.count() == 0)
{
QMessageBox::information(this,"Directory is empty","Empty!!!");
}
}
Whats the right way to check it, excluding . and ..?

Well, I got the way to do it :)
if(QDir("/home/highlander/Desktop/dir").entryInfoList(QDir::NoDotAndDotDot|QDir::AllEntries).count() == 0)
{
QMessageBox::information(this,"Directory is empty","Empty!!!");
}

Since Qt 5.9 there is bool QDir::isEmpty(...), which is preferable as it is clearer and faster, see docs:
Equivalent to count() == 0 with filters QDir::AllEntries | QDir::NoDotAndDotDot, but faster as it just checks whether the directory contains at least one entry.

As Kirinyale pointed out, hidden and system files (like socket files)
are not counted in highlander141's answer.
To count these as well, consider the following method:
bool dirIsEmpty(const QDir& _dir)
{
QFileInfoList infoList = _dir.entryInfoList(QDir::AllEntries | QDir::System | QDir::NoDotAndDotDot | QDir::Hidden );
return infoList.isEmpty();
}

This is one way of doing it.
#include <QCoreApplication>
#include <QDir>
#include <QDebug>
#include <QDesktopServices>
int main(int argc, char *argv[])
{
QCoreApplication app(argc,argv);
QDir dir(QDesktopServices::storageLocation(QDesktopServices::DesktopLocation));
QStringList list = dir.entryList();
int count;
for(int x=0;x<list.count(); x++)
{
if(list.at(x) != "." && list.at(x) != "..")
{
count++;
}
}
qDebug() << "This directory has " << count << " files in it.";
return 0;
}

Or you could just check with;
if(dir.count()<3){
... //empty dir
}

Related

remove line from Qstring

I have a QString with 5 lines
"AAAAAR"
"BBBBB"
"CCCRC"
"DDDDD"
"EEEEE"
I would like to search 'R' character only with lines from 2 to 4 If one of them contents 'R' then remove the line from my QString
I tried many method but without successs.
You can split the string for each line, giving you a QStringList with 5 strings in it. Then iterate from 2 to 4 and if the string contains R, remove the string from the string list, and in the end, join the string list back again.
QString s("AAAAAR\nBBBBB\nCCCRC\nDDDDD\nEEEEE");
QStringList l = s.split('\n');
int i = 1, stop = 4;
while (i < stop) {
if (!l[i].contains('R')) ++i;
else {
l.removeAt(i);
--stop;
}
}
s = l.join('\n');
This solution should be more flexible and work regardless of the input, just select the range you want the check in.
Ironically enough, if the loop is iterated in reverse there is no problem with leaving holes and keeping track of the total count.
for (int i = 3; i > 0; --i) if (l[i].contains('R')) l.removeAt(i);
Similar to ddriver's solution but using iterators
#include <QApplication>
#include <QString>
#include <QStringList>
#include <QtCore>
int main(int argc, char** argv)
{
QApplication app(argc,argv);
QString s("AAAAAR\nBBBBB\nCCCRC\nDDDDD\nEEEEE");
QStringList l = s.split('\n');
for( QStringList::iterator it = ++l.begin(); it != --l.end(); ) {
if( it->contains('R') ) {
it = l.erase(it);
} else {
++it;
}
}
s = l.join("\n");
qDebug() << l;
return app.exec();
}
Output:
("AAAAAR", "BBBBB", "DDDDD", "EEEEE")
I don't use in increment in the for loop but instead use
QList::erase(iterator pos) which Removes the item associated with the iterator pos from the list, and returns an iterator to the next item in the list.
I assume more generically that you want to loop from the 2nd to the second to last element, irrespective of the number of elements.
EDIT: in case you want to move a specific element in the container you could e.g. use std::advance
#include <QApplication>
#include <QString>
#include <QStringList>
#include <QtCore>
#include <iterator>
int main(int argc, char** argv)
{
QApplication app(argc,argv);
QString s("AAAAAR\nBBBBB\nCCCRC\nDDDDD\nEEEEE");
QStringList l = s.split('\n');
QStringList::iterator it = l.begin();
QStringList::iterator itEnd = l.begin();
std::advance(it,2); // set the number to whatever start position you want
std::advance(itEnd,4); // set the number to whatever end position you want
for( ; it != itEnd ; ) {
if( it->contains('R') ) {
it = l.erase(it);
} else {
++it;
}
}
s = l.join("\n");
return app.exec();
}

Renaming files with Qt

I'm trying to write a program that renames a certain list of files in a chosen directory with a new extension. Pretty much to replace all .dx90 files with .dx80 files. This is the code that I've written so far and it's not working. All the files are being placed into the failed files list but I'm getting no errors.
#include <QFileDialog>
#include <QString>
#include <QApplication>
#include <QDir>
#include <QStringList>
#include <QTextStream>
QTextStream cout(stdout);
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
QString dirName = QFileDialog::getExistingDirectory(0, "Open Directory", QDir::currentPath(), QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
QDir directory(dirName);
QStringList filters;
filters << "*.dx90";
QStringList files = directory.entryList(filters);
QStringList changedFiles, failedFiles;
foreach(QString filename, files)
{
QFileInfo info(filename);
QString rawFileName = filename.section(".", 0, 0);
QString newName = info.absoluteFilePath().section("/", 0, -2) + "/" + rawFileName + ".dx80";
bool success = directory.rename(info.absoluteFilePath(), newName);
if(success)
{
changedFiles << info.absoluteFilePath();
}
else
{
failedFiles << info.absoluteFilePath();
}
}
return 0;
}
I figured it out. The error I made was in the line:
QFileInfo info(filename);
It was not finding the file as the variable filename was not the absolute path. This made the info variable default to the QFileInfo of the current working directory of the application. I fixed the code by changing that line to:
QFileInfo info(directory.absolutePath() + "/" + filename);
Thanks to anyone that tried to help me fix the code. I hope this helps others that have similar problems.

Recursive search for directory returning incorrect value

Having a problem returning the correct value from a recursive search for a directory. The code is below
#include <QCoreApplication>
#include <QDir>
#include <QString>
#include <QDebug>
static QString findDirectoryPathFromId(const QString &startPath, const QString &id)
{
QDir dir(startPath);
QFileInfoList list = dir.entryInfoList(QDir::AllDirs | QDir::NoDotAndDotDot | QDir::NoSymLinks);
QString path;
foreach(QFileInfo dinfo, list)
{
if (dinfo.fileName() == id)
{
qDebug() << "****************Found****************" << dinfo.filePath();
return dinfo.filePath();
}
else
{
findDirectoryPathFromId(dinfo.absoluteFilePath(), id);
}
}
return QString();
}
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
QStringList args = app.arguments();
QString path = findDirectoryPathFromId("/home/project/dirtest", args.at(1));
qDebug() << "Return path" << path;
return 0;
}
The function finds the directory as the "Found" debug statement is printed, however the return value is a null string.
Could someobody explain what I'm doing wrong here.
Thanks
I think I've fixed it.
I need to check if the recursive call has found the directory and return it.
path = findDirectoryPathFromId(dinfo.absoluteFilePath(), id);
if (!path.isNull())
return path;
Is this correct.

limit directory traversal in QFileDialog

I'm using QFileDialog to select a directory. I'm having an issue that I'm unable to resolve. I've spent a lot of time googling for this but have come up with zilch.
I specify the starting directory (say /home/dhoti/downloads) and I want to disable navigation above this directory. For example the user should not be allowed to go to /home/dhoti or /tmp etc. How do I achieve this?
Here is my code:
QFileDialog dlg(this, "Select Firmware Version");
dlg.setDirectory("/home/dhoti/downloads");
dlg.setFileMode(QFileDialog::DirectoryOnly);
dlg.setOption(QFileDialog::ReadOnly, true);
dlg.setOption(QFileDialog::HideNameFilterDetails, true);
dlg.setViewMode(QFileDialog::List);
dlg.setAcceptMode(QFileDialog::AcceptOpen);
dlg.exec();
qDebug() << "selected files: " << dlg.selectedFiles();
thanks for any help
Dhoti
You can detect when the current directory changes and if it is beyond your limit, set the directory back to the limit directory.
You can do this by executing the dialog non-blocking, and connecting the QFileDialog::directoryEntered(const QString& directory) signal to a slot of your own where you can do the checking. If it fails your check, set the current directory to the limit directory by QFileDialog::setDirectory(const QString& directory).
Disclaimer I have not tried this, but I'll be surprised if it does not work.
Try the following:
filedialog.h
#ifndef FILEDIALOG_H
#define FILEDIALOG_H
class QEvent;
#include <QFileDialog>
#include <QString>
class FileDialog : public QFileDialog
{
Q_OBJECT
public:
explicit FileDialog(QWidget *parent = 0);
public:
bool eventFilter(QObject *o, QEvent *e);
void setTopDir(const QString &path);
QString topDir() const;
private:
bool pathFits(const QString &path) const;
private slots:
void checkHistory();
void checkGoToParent();
void checkLineEdit(const QString &text);
private:
QString mtopDir;
};
#endif // FILEDIALOG_H
filedialog.cpp
#include "filedialog.h"
#include <QString>
#include <QStringList>
#include <QFileDialog>
#include <QList>
#include <QToolButton>
#include <QDir>
#include <QLineEdit>
#include <QDialogButtonBox>
#include <QEvent>
#include <QKeyEvent>
#include <QAbstractButton>
#include <QCompleter>
#include <QAbstractItemView>
#include <QFileInfo>
FileDialog::FileDialog(QWidget *parent) :
QFileDialog(parent)
{
connect(this, SIGNAL(directoryEntered(QString)), this, SLOT(checkHistory()));
connect(this, SIGNAL(directoryEntered(QString)), this, SLOT(checkGoToParent()));
connect(findChild<QToolButton *>("backButton"), SIGNAL(clicked()), this, SLOT(checkGoToParent()));
connect(findChild<QToolButton *>("forwardButton"), SIGNAL(clicked()), this, SLOT(checkGoToParent()));
connect(findChild<QLineEdit *>("fileNameEdit"), SIGNAL(textChanged(QString)), this, SLOT(checkLineEdit(QString)));
findChild<QLineEdit *>("fileNameEdit")->installEventFilter(this);
findChild<QWidget *>("listView")->installEventFilter(this);
findChild<QWidget *>("treeView")->installEventFilter(this);
findChild<QLineEdit *>("fileNameEdit")->completer()->popup()->installEventFilter(this);
setOption(DontUseNativeDialog, true);
}
bool FileDialog::eventFilter(QObject *o, QEvent *e)
{
if (e->type() != QEvent::KeyPress)
return false;
int key = static_cast<QKeyEvent *>(e)->key();
if (o->objectName() == "listView" || o->objectName() == "treeView")
{
return (Qt::Key_Backspace == key && !pathFits(directory().absolutePath()));
}
else
{
if (Qt::Key_Return != key && Qt::Key_Enter != key)
return false;
QString text = findChild<QLineEdit *>("fileNameEdit")->text();
QString path = QDir::cleanPath(directory().absolutePath() + (text.startsWith("/") ? "" : "/") + text);
bool a = QDir(text).isAbsolute();
return !((!a && pathFits(path)) || (a && pathFits(text)));
}
}
void FileDialog::setTopDir(const QString &path)
{
if (path == mtopDir)
return;
mtopDir = (!path.isEmpty() && QFileInfo(path).isDir()) ? path : QString();
if (!pathFits(path))
{
setDirectory(mtopDir);
checkHistory();
checkLineEdit(findChild<QLineEdit *>("fileNameEdit")->text());
}
else
{
QLineEdit *ledt = findChild<QLineEdit *>("fileNameEdit");
ledt->setText(ledt->text());
}
findChild<QWidget *>("lookInCombo")->setEnabled(mtopDir.isEmpty());
findChild<QWidget *>("sidebar")->setEnabled(mtopDir.isEmpty());
checkGoToParent();
}
QString FileDialog::topDir() const
{
return mtopDir;
}
bool FileDialog::pathFits(const QString &path) const
{
return mtopDir.isEmpty() || (path.startsWith(mtopDir) && path.length() > mtopDir.length());
}
void FileDialog::checkHistory()
{
QStringList list = history();
for (int i = list.size() - 1; i >= 0; --i)
if (!pathFits(list.at(i)))
list.removeAt(i);
setHistory(list);
}
void FileDialog::checkGoToParent()
{
findChild<QToolButton *>("toParentButton")->setEnabled(pathFits(directory().absolutePath()));
}
void FileDialog::checkLineEdit(const QString &text)
{
QAbstractButton *btn = findChild<QDialogButtonBox *>("buttonBox")->buttons().first();
QString path = QDir::cleanPath(directory().absolutePath() + (text.startsWith("/") ? "" : "/") + text);
bool a = QDir(text).isAbsolute();
btn->setEnabled(btn->isEnabled() && ((!a && pathFits(path)) || (a && pathFits(text))));
}
This code may look like some magic, and it's not perfect, but it works. I searched for QFileDialog child objects names in Qt sources and used
findChild()
method to access them. All you need is just use the
setTopDir()
method to specify a directory above which users are not allowed to go.
Here's an example project using this class: https://docs.google.com/file/d/0B3P3dwuDIZ1-Q19FbkFMY2puUE0/edit?usp=sharing
You can use the solution of #ololoepepe. And clean unwanted entries in the comboBox on the top with this:
connect(findChild<QComboBox *>("lookInCombo"), static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &FileDialog::checkComboBox);
void FileDialog::checkComboBox(int index) {
int i;
QComboBox *cb = findChild<QComboBox *>("lookInCombo");
if (index == 0 && cb->model()->rowCount() > 1) {
for (i = 0; i < cb->model()->rowCount(); ++i) {
if (!pathFits(cb->model()->index(i, 0).data().toString() + "/")) {
cb->model()->removeRow(i);
--i;
}
}
}
}
Here is the simplest solution, with the minimum steps required to limit a directory traversal.
Idea: use public signal directoryEntered(const QString &) of QFileDialog to get notification when directory might be changed, implement slot for it in one of your classes and place there a logic for making sure that directory is the one you need.
QFileDialog dialog(this);
connect(&dialog, SIGNAL(directoryEntered(const QString &)), this, SLOT(onFileDialogDirectoryChanged(const QString &)));

Save QList<int> to QSettings

I want to save a QList<int> to my QSettings without looping through it.
I know that I could use writeArray() and a loop to save all items or to write the QList to a QByteArray and save this but then it is not human readable in my INI file..
Currently I am using the following to transform my QList<int> to QList<QVariant>:
QList<QVariant> variantList;
//Temp is the QList<int>
for (int i = 0; i < temp.size(); i++)
variantList.append(temp.at(i));
And to save this QList<Variant> to my Settings I use the following code:
QVariant list;
list.setValue(variantList);
//saveSession is my QSettings object
saveSession.setValue("MyList", list);
The QList is correctly saved to my INI file as I can see (comma seperated list of my ints)
But the function crashes on exit.
I already tried to use a pointer to my QSettings object instead but then it crashes on deleting the pointer ..
QSettings::setValue() needs QVariant as a second parameter. To pass QList as QVariant, you have to declare it as a Qt meta type. Here's the code snippet that demonstrates how to register a type as meta type:
#include <QCoreApplication>
#include <QDebug>
#include <QMetaType>
#include <QSettings>
#include <QVariant>
Q_DECLARE_METATYPE(QList<int>)
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qRegisterMetaTypeStreamOperators<QList<int> >("QList<int>");
QList<int> myList;
myList.append(1);
myList.append(2);
myList.append(3);
QSettings settings("Moose Soft", "Facturo-Pro");
settings.setValue("foo", QVariant::fromValue(myList));
QList<int> myList2 = settings.value("foo").value<QList<int> >();
qDebug() << myList2;
return 0;
}
You might have to register QList as a meta-type of its own for it to work. This is a good starting point to read up on meta-types in Qt: http://qt.nokia.com/doc/4.6/qmetatype.html#details .
I was also struggling with this ... and I believe I now have a decent solution.
I hope this saves someone the trouble, it caused me.
#include <QCoreApplication>
#include <QSettings>
#include <QList>
#include <QDataStream>
#include <QVariant>
#include <QVariantList>
#include <QDebug>
#include <deque>
template <class T> static QVariant toVariant(const QList<T> &list)
{
QVariantList variantList;
variantList.reserve(list.size());
for (const auto& v : list)
{
variantList.append(v);
}
return variantList;
}
template <class T> static QList<T> toList(const QVariant &qv)
{
QList <T> dataList;
foreach(QVariant v, qv.value<QVariantList>()) {
dataList << v.value<T>();
}
return dataList;
}
void Gen()
{
QList<QString> data {"hello", "world","how", "are", "you"};
QList<int> ages {10,20,30,40};
QSettings setup("stuff.ini", QSettings::IniFormat);
setup.beginWriteArray("rules");
for (int groups=0;groups<3;groups++)
{
setup.setArrayIndex(groups);
setup.setValue("rule",QString("Rule-%1").arg(groups));
setup.setValue("data", toVariant (data));
setup.setValue("ages", toVariant (ages));
}
setup.endArray();
setup.sync();
}
void Read()
{
QSettings setupR("stuff.ini", QSettings::IniFormat);
int rule_count = setupR.beginReadArray("rules");
for (int groups=0;groups<rule_count;groups++)
{
setupR.setArrayIndex(groups);
QString nameRead = setupR.value("rule").toString();
QList<QString> dataRead = toList<QString>(setupR.value("data"));
qDebug() << "Rule " << groups;
qDebug() << "Rule Read" << nameRead;
qDebug() << "Data Read2" << dataRead;
}
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// Write
Gen();
// Read
Read();
exit(0);
//return a.exec();
}
You should end up with an INI file which looks like this ...
[rules]
1\ages=10, 20, 30, 40
1\data=hello, world, how, are, you
1\rule=Rule-0
2\ages=10, 20, 30, 40
2\data=hello, world, how, are, you
2\rule=Rule-1
3\ages=10, 20, 30, 40
3\data=hello, world, how, are, you
3\rule=Rule-2
size=3
The nice thing here is you can edit this outside of QT (Just be careful) ...

Resources