What is the best way to store multiple QFile in a collection? Would be best to use QList, array, or vectors?
I want to access the element of the collection as a QFile and not QFile*.
I'm trying to read a directory in Qt and store all the files in a collection. Then print all the file names to the screen.
How I'm reading the directory:
QDirIterator it(p_dir, QStringList() << "*.wav", QDir::Files, QDirIterator::Subdirectories);
while (it.hasNext())
{
// Put file into collection
}
Currently I'm using QList to store the QFiles:
QList<QFile*> list;
I read that the best way to get file information is to use QFileInfo type. How could I set the QFileInfo file to be the element from my list?
I want to do something like:
QFileInfo info;
QListIterator<QFile*> i(list);
while(i.hasNext())
{
info.setFile(i);
}
But the above gives me:
error: no matching function for call to 'QFileInfo::setFile(QListIterator&)'
fi.setFile(i);
Keep in mind QFile inherits QObject, and as such is not copyable or movable. It is common practice to store pointers to such objects in containers rather than the objects themselves. As for which container to use, it doesn't really matter much, but QVector will be the most efficient.
QListIterator is a Java style iterator. So:
QListIterator::QListIterator(const QList<T> & list)
Constructs an iterator for traversing list. The iterator is set to be
at the front of the list (before the first item).
The iterator doesn't point to anything, if you want to get the actual object, use i.next() to give you a QFile* which you then will have to dereference, since setFile() takes in a QFile &, not a QFile *.
QFile is not copyable. In C++98, there's generally no way of storing it in a container, unless the container supports in-place construction. I don't know offhand of any such C++98 containers, although writing one wouldn't be all that hard.
In C++11, you can use any container that doesn't need to copy elements and supports emplacement, e.g. std::list:
// https://github.com/KubaO/stackoverflown/tree/master/questions/qfile-list-36391586
#include <QtCore>
#include <list>
void populate(std::list<QFile> & files) {
QDir dir(QCoreApplication::applicationDirPath());
for (auto fileName : dir.entryList(QDir::Files)) {
qDebug() << "adding file" << fileName;
files.emplace_back(fileName);
}
}
void iterate(std::list<QFile> & files) {
for (auto & file : files)
if (file.open(QIODevice::ReadOnly)) {
qDebug() << "successfully opened" << file.fileName();
}
}
int main(int argc, char ** argv) {
QCoreApplication app(argc, argv);
std::list<QFile> files;
populate(files);
iterate(files);
}
On my system, when run from Qt Creator, the output is:
adding file "main.o"
adding file "Makefile"
adding file "qfile-list-36391586"
successfully opened "main.o"
successfully opened "Makefile"
successfully opened "qfile-list-36391586"
Since QFile is a proper class implementing RAII of system resources, the files will get automatically closed by files::~list, before main returns.
I'm currently manipulating QFileInfo based on certain criterian/logics..but if I just filter all the complicated logic, it would be something like
QDir testDir("/home/mypath/to/dir");
QFileInfoList files = testDir.entryInfoList(); // #1. here you may use filters as I'm filtering based on a specific file extension
QFileInfoList myfiles;
for(int index = 0; index < files.size(); ++index)
{
const QFileInfo& info = files.at(index);
qDebug() << info.absoluteFilePath() << endl;
// #2. I've some sort of filters I used in #1 above that I compares here by utilizing info here, something like
//if (info.baseName().contains("anyPattern");
myfiles.push_back(info);
}
// Now you can view whatever you stored
for (int vw_idx = 0; vw_idx < myfiles.size(); ++vw_idx)
{
const QFileInfo& info = files.at(index);
qDebug() << info.absoluteFilePath() << endl;
}
Related
I have a understanding-problem why following code does not store any QString-Objects into my QList
QMap<QString, QList<QString> >map;
map = QMap<QString, QList<QString> >();
map.insert("eins", QList<QString>());
QList<QString> listInMap = map["eins"];
listInMap.append("test1");
listInMap.append("test2");
QList<QString> copyOfListInMap = map.value("eins");
qDebug() << copyOfListInMap.count();
Output: 0
The reason is simple: copy on write, aka. implicit sharing
QList<QString> listInMap = map["eins"];
At this point, you have not yet got a hard copy, only a "reference". That is not fair in the standard C++ sense, but imagine it as a "shallow copy". However, when you start appending in here, the list will copied and the original will left empty. It is because QList is implemented the way that is CoW (copy-on-write).
listInMap.append("test1");
listInMap.append("test2");
On a side note, you may wish to take a look at QStringList. While it inherits QList, it also has some additional convenience methods.
Now, you may be asking: How am I supposed to fill my map in, then?.
Multi map
I would personally suggest to use QMultiMap or at least QMap with insertMulti.
main.cpp
#include <QMap>
#include <QDebug>
#include <QString>
int main()
{
QMultiMap<QString, QString> map;
map.insert("eins", "test1");
map.insert("eins", "test2");
qDebug() << map.count();
return 0;
}
main.pro
TEMPLATE = app
TARGET = main
QT = core
SOURCES += main.cpp
Build and Run
qmake && make && ./main
Output
2
Single map
If you stick to the current approach, I would suggest to either append the value into a new QStringList with which you will overwrite the value, or hold a reference to the exact same list.
Having said that, in your case, it looks a bit overkill to even consider an external storage for insertion. You ought to do that right away in my humble opinion.
main.cpp
#include <QMap>
#include <QDebug>
#include <QString>
int main()
{
QMap<QString, QStringList> map;
map.insert("eins", QStringList{"test1", "test2"});
qDebug() << map.value("eins").count();
return 0;
}
main.pro
TEMPLATE = app
TARGET = main
QT = core
CONFIG += c++11
SOURCES += main.cpp
Build and Run
qmake && make && ./main
Output
2
According to the documentation your sample should be changed as follow:
QMap<QString, QList<QString> >map;
QList<QString> & listInMap = map["eins"]; // use reference here
listInMap.append("test1");
listInMap.append("test2");
QList<QString> copyOfListInMap = map.value("eins");
qDebug() << copyOfListInMap.count();
Hi I'm trying to iterate through directories and remove files with the extension "~" here is my code
QString path = "/home/brett/sweetback";
QDirIterator it(path, QDirIterator::Subdirectories);
while (it.hasNext()) {
//ui.textEdit->append(QString(it.next()));
QFileInfo Info(it.next());
//ui.textEdit->append(QString(Info.fileName()));
QString testName = QString(Info.fileName());
QString subString = testName.right(1);
if(subString == QString("~")){
//wnat to remove file here
ui.textEdit->append(QString(subString));
remove(QString( testName));
}
}
I can list the file fine but cant figure out how to delete them
I think you're looking for QFile::remove()
It's a static member of QFile, so you would use it like this:
QFile::remove(testName);
bool QFile::remove(const QString & fileName) [static]
This is an overloaded function.
Removes the file specified by the fileName given.
Returns true if successful; otherwise returns false.
So, change your code:
remove(QString( testName));
to:
if (!QFile::remove(testName))
qDebug() << "Could not remove the file:" << testName;
Note that you do not need to cast a QString to QString explicitly. That is superfluous.
You could also use the non-static member method, and then you could even get the error string by using errorString() for the QFile instance when the deletion is not successful.
If you would also like to delete whole directories recursively having the desired ~ suffix, you would need to use the remove member method in QDir for such cases.
For just practice, using QT library I am trying to select a folder and list the name of the dicom files in that folder. The following is code for that:
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
QString fileName = QFileDialog::getExistingDirectory();
if (fileName.isEmpty())
{
std::cout << "Got an empty file!\n";
}
else
{
QStringList nameFilter;
QDir dir(fileName);
nameFilter << "*.dcm";
QFileInfoList list = dir.entryInfoList( nameFilter, QDir::Files );
int numberOfFiles=list.count();
for(int i=0;i<numberOfFiles;i++)
{
QString filena=list[i].fileName();
string a=filena.toStdString();
cout<<a<<endl;
}
}
return 0;
}
Here I have found out that the function toStdString, which is actually std::string QString::toStdString () const, gives Heap error. I know the replacement to get rid of this error is to use toLocal8Bit().constData(), but I'm curious what's the main reason behind the heap error provided by toStdString. Is it because it is const type and my for loop is trying overwrite the const variable everytime?
Your code looks good - make sure your Qt's dll files are compiled with the same compiler (and with same Debug/Release configuration) you are using.
To answer some of your questions:
Is it because it is const type and my for loop is trying overwrite the const variable everytime?
No, your for loop is not trying to overwrite const variable. Const variable is on the right side of the assingment operator, so your for loop is reading from const variable, and it doesn't overwritting anything (your a variable is local variable visible inside for loop's block, so a is different in each pass of the loop).
Even if you try to overwrite const variable, that would not compile - trying to change const will break in compile-time, and not in runtime.
This code works for me.
You'd better not convert strings to std::string and use std::cout, but rather use QTextStream for output:
QTextStream out(stdout);
//...
QString filena=list[i].fileName();
out << filena << endl;
My money would be on mixing Debug versions of the Qt Framework with Release versions of your compiled program.
Especially under Windows a different heap manager is used in debug and release builds.
Try recompiling in debug mode and seeing what happens.
i have a strange problem with QList and the boost:shared_ptr. I'm afraid i am not able to strip the problem apart, that's why i will post the wholoe function later on.
What i want to do: I have a list (_fileList) which stores boost::shared ptrs to QFile Objects. The files are XML files. Than i want to parse this file and resolve all includes which means adding the files specified by the include tag also to the _fileList and scanning them for further include tags. The code works fine when resolving 3 includes (in my small test there is only one include per file). The third time the line
boost::shared_ptr file(*iter); leads to a segmentation fault.
I would be glad if anyone of you can help me or give me hints how i can find this bug.
void XmlParser::expandIncludes()
{
//search all files already in the file list for include tags, if any new are found append this file to the file list
QList<boost::shared_ptr<QFile> >::iterator iter = this->_fileList.begin();
while(iter!= this->_fileList.end())
{
boost::shared_ptr<QFile> file(*iter);
QDomDocument doc("activeFile");
if (!file->open(QIODevice::ReadOnly)){
return;
}
if (!doc.setContent(&(*file))) {
file->close();
return;
}
file->close();
QDomElement docElem = doc.documentElement();
QDomNode n = docElem.firstChildElement("include");
while(!n.isNull()) {
QDomElement e = n.toElement(); // try to convert the node to an element.
if(!e.isNull()) {
QString nextFile = e.text();
QString nextFileAbsolutePath = this->_workingDir.absolutePath() +"/"+nextFile;
boost::shared_ptr<QFile> newFileObject(new QFile(nextFileAbsolutePath));
this->_fileList.append(newFileObject);
}
n = n.nextSiblingElement("include");
}
doc.clear();
iter++;
}
}
Iterators pointing to elements in a QList becomes invalid after you insert new elements into the list. You could use a QLinkList instead.
From the Qt Container docs:
Iterators pointing to an item in a QLinkedList remain valid as long as the item exists, whereas iterators to a QList can become invalid after any insertion or removal.
After reading Save QList<int> to QSettings, I'm tring to do the same with QMap<QString,int>. I would like the configuration file to look like this:
1111=1
2222=3
4444=0
But I'm getting a compilation error:
Q_DECLARE_METATYPE(QMap<QString,int>)
Warning C4002: too many actual parameters for macro 'Q_DECLARE_METATYPE'
ConfigSettings.h(5) : error C2976: 'QMap' : too few template arguments
The error message you're getting is caused by the fact that the preprocessor doesn't know about templates. So it's parsing that macro call is if it had two arguments - QMap<QString and int>, which makes no sense.
To save the data as you want it, you're better of serializing it yourself to your QSettings. Something like this for writing:
settings.beginGroup("Whatever");
QMap<QString, int>::const_iterator i = map.constBegin();
while (i != map.constEnd()) {
settings.setValue(i.key(), i.value());
++i;
}
settings.endGroup();
To read the settings, use the same approach with the help of the childKeys() function.
settings.beginGroup("Whatever");
QStringList keys = settings.childKeys();
foreach (QString key, keys) {
map[key] = settings.value(key).toInt();
}
settings.endGroup();
Like Mat said, the error is caused by the preprocessor not understanding templates. However, you can easily fix this via a simple typedef.
typedef QMap<QString,int> QIntMap
Q_DECLARE_METATYPE(QIntMap)
QSetting accept QVariant type to pass into setValue method, so it means that you can store QMap<QString, QVarint> map directly to settings
// Store
QMap<QString, QVariant> storeMap;
QMapIterator it(myMap);
// iterate through the map to save the values in your chosen format
while(it.hasNext())
{
storeMap[it.key()] = QVariant(it.value());
it.next();
}
settings.setValue("myKey", storeMap);
..
// Read
QMap<QString, QVariant> readMap = settings.value("myKey").toMap();
QMapIterator it(readMap);
while(it.hasNext())
{
myMap[it.key()] = it.value().toInt();
it.next();
}
I understand the accepted answer, but I think the original question was how to store the QMap. It devolved into how to make the compiler behave.
QSettings mySettings...
QMapIterator it(myMap);
// iterate through the map to save the values in your chosen format
while(it.hasNext())
{
it.next();
mySettings.setValue(it.key(), it.value());
}
If however you wish to store this along with a bunch of other settings or data structures, you might consider using "beginGroup()" and "endGroup()" to group a bunch of different data structures into one file. Very tidy and readable.