Qt - c++ : about how to use QFileInfo without object - c++

I'm sorry if this was asked before but I couldn't get the right words in search and that might have effected the search results , anyways I returned to c++ and I decided to program a simple tool , while I was programming I needed a way to search for a file in Qt , this is the code:
void Main_Sorter::SearchForFile(QString Path , QString extention , QString FileName)
{
QDirIterator dir(Path , QDirIterator::Subdirectories);
while(dir.hasNext())
{
dir.next();
if(QFileInfo(dir.filePath()).isFile())
if(QFileInfo(dir.filePath()).suffix() == extention)
if(QFileInfo(dir.filePath()).fileName() == FileName)
qDebug()<<"file found ! " + FileName;
}
}
I was wondering how are we using the class name alone here ? " QFileInfo " without an object to control it ? and how did we get an QFileInfo object from
QFileInfo(dir.filePath())
it's been too long since I used c++ but I don't remember using such things ?

To access static class data you can use :: operator, like
QFileInfo::someStaticData
Moreover, by typing
if(QFileInfo(dir.filePath()).isFile())
you are creating temporary QFileInfo object, which is deleted right after the if statement.
What you would probably want is to save the object to some local variable, like:
QFileInfo fileInfo(dir.filePath());
and use that variable in if statements:
if(fileInfo.isFile()) {
if(fileInfo.suffix() == extention)
....
}

Related

Can't convert QString to Const Char*

I'm new to QT so please excuse me if I'm blatantly doing something wrong here, but I've looked at all the questions here on the matter but can't seem to find something that works. I'm trying to have the user create a folder by entering a name for it, and it 'creates' a folder with the name. I say 'create' because it's not exactly creating one, it makes a folder first called "project" before you enter the name, and when you enter a name it renames it. However, when I try and rename the folder with the inputted name it gives me
error:C2664: 'int rename(const char *,const char *)' : cannot convert
argument 1 from 'QString' to 'const char *'
Here's my code:
void MainWindow::on_actionNew_Project_triggered(const char *parameter)
{
//Create project folder
QString projectPath = "D:/Project";
QDir dir(projectPath);
if (!dir.exists()) {
dir.mkpath(projectPath);
}
//Get project name from user
bool result;
QString name = QInputDialog::getText(0, "New Project",
"Enter in project name", QLineEdit::Normal,
"", &result);
if(result && !name.isEmpty()) {
//Rename project folder to user created name
QDir dir(projectPath);
if (dir.exists()) {
rename(projectPath, name); //Gives me error HERE
}
}
}
I would appreciate it if you guys could help, I've been stuck on this for hours.
You could call QString::toStdString(), and then call c_str() to retrieve the const char* from the std::string.
Your code would look something like this:
if (dir.exists()) {
rename(projectPath.toStdString().c_str(), name);
}
Try dir.rename(dir.dirName(), name);
You are trying to invoke a member function without an instance.
Since rename() is a member function of QDir, you need a QDir instance in order to invoke it. So rather than just calling rename() which invokes who knows what, you need to dir.rename().
QDir::rename() actually takes 2 QStrings as parameters, but that other function you are invoking takes two raw strings, so you don't really need to convert the strings, you were just calling the wrong function.
bool QDir::rename(const QString & oldName, const QString & newName)
You are most likely calling rename() from <stdio.h>, which could also work given that the parameters are correct and the OS can rename the directory, in that case you will need to convert to "raw" C-style strings via yourString.toLatin1().constData(). But since you are using Qt, you might as well use the QDir API, which works directly with QString.
If it still doesn't work, then either your input parameters are wrong, or there is something preventing the OS from rename the directory, for example a file currently in use.
Qt FAQ says:
int main(int argc, char **argv)
{
QApplication app(argc, argv);
QString str1 = "Test";
QByteArray ba = str1.toLatin1();
const char *c_str2 = ba.data();
printf("str2: %s", c_str2);
return app.exec();
}

Properly storing QFiles in a collection

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;
}

Delete file in directory with extension

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.

Heap error in for loop using const type variable?

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.

How do I get a string or stream into a CStreamFile?

this question may seem a bit too specific, but I figure I'd give it a shot here since I've found some great programming answers here in the past.
I am modifying the open-source program TinyCad for a project I'm working on. I've visited the TinyCad message board and posted, but I didn't get the answer I'm looking for. I'm having trouble wrapping my head about how to integrate a small XML converter class I wrote into the loading function of TinyCad.
A little background about me: I have no experience with MFC or Visual Studio, but that is what I have to use. I am used to C++ and was taught using iostream syntax (cout, cin, new, etc.) so I'm not used to older C code (like printf, sprintf, malloc, alloc, etc.) either. I usually write my programs from start to finish in Qt, but I was told that for this project I should modify an existing program to save time. I don't know if it'll save that much time if I have to learn something totally foreign, but I digress.
I wrote a small class to read in an XML file that is structured differently than the XML file that TinyCad reads in. My class converts it and outputs an intermediate XML file. Well, I don't want to spit out an intermediate file. I modified it to save the output as a string (using the string datatype from the standard C++ iostream library). I want to get this string into a stream so that TinyCad can open the file, do the conversion, and then continue loading.
My class is called like so:
std::string blah;
char* filename = "library.xml";
XMLopen myXML(filename, blah);
So it takes in a filename, opens the file, parses the relevant information out of the file, puts the information into TinyCad's XML structure, and saves the XML code as a string that has been passed by reference.
I had an idea to use istringstream to make a stream, but that did not play nice with CFile. I tried it like so:
istringstream ins; // Declare an input string stream.
ins.str(blah);
// First open the stream to save to
CFile theFile(ins);
Below is the code in TinyCad that opens and loads the selected XML file:
void CLibraryStore::LoadXML( const TCHAR *filename )
{
// First open the stream to save to
CFile theFile;
// Open the file for saving as a CFile for a CArchive
BOOL r = theFile.Open(filename, CFile::modeRead);
if (r)
{
CString name;
// Create the XML stream writer
CStreamFile stream( &theFile, CArchive::load );
CXMLReader xml( &stream );
// Get the library tag
xml.nextTag( name );
if (name != "Library")
{
Message(IDS_ABORTVERSION,MB_ICONEXCLAMATION);
return;
}
xml.intoTag();
CTinyCadApp::SetLockOutSymbolRedraw( true );
while ( xml.nextTag( name ) )
{
// Is this a symbol?
if (name == "SYMBOL")
{
// Load in the details
xml.intoTag();
CTinyCadMultiSymbolDoc temp_doc;
drawingCollection drawing;
CLibraryStoreNameSet s;
// this is where the stream gets sent to be loaded into the data structure
s.LoadXML( &temp_doc, xml );
xml.outofTag();
// ... and store the symbol
Store( &s, temp_doc );
}
}
xml.outofTag();
CTinyCadApp::SetLockOutSymbolRedraw( false );
}
}
Edit 7/28/2010 5:55PM
So I tried to make a stream, but it fails.
CStreamFile takes in a filename and then gets set as a CArchive:
m_pArchive = new CArchive( theFile, nmode );
I tried to make a CStream like so (since CStreamFile is an overloaded CStream):
CString test = blah.c_str();
CStreamMemory streamCS;
streamCS << test;
CXMLReader xml( &streamCS );
But at streamCS << test; it doesn't put the stream in at all. test gets assigned correctly with blah so I know that's working.
Any ideas on how to approach this?