Qt5::QFileSystemWatcher to invoke on file modification - c++

QFileSystemWatcher watcher;
watcher.addPath("C:/watch");
QStringList directoryList = watcher.directories();
Q_FOREACH(QString directory, directoryList)
qDebug() << "Directory name" << directory <<"\n";
DirectoryWatcher* dw = new DirectoryWatcher;
QObject::connect(
&watcher, SIGNAL(directoryChanged(const QString&)),
dw, SLOT(modified(const QString&))
);
QObject::connect(
&watcher, SIGNAL(fileChanged(QString)),
dw, SLOT(modified(QString))
);
In this sample, modified() method called when;
a new file created
a file deleted
a file renamed
But, If i open a file in this folder and modify the content, after I save it, nothing called.
IF I add that specific file to the path like addPath("c:/watch/me.txt") then after modify it gets invoked.
But as you might know, there is a limitation on watcher. So I cannot watch hundreds of files every time.
How can I invoke modified() method on file modifications?

If you want a cross-platform solution, using Qt5::QFileSystemWatcher, you have no other way than adding each files from the directory you're watching to the QFileSystemWatcher object, hoping that you don't hit the file descriptors limitation.
If you want to use OS specific methods to watch filesystem, you can get some hints from this S/O answer : https://stackoverflow.com/a/931165/228634 but I'm pretty sure you'll have the same limitations.

Related

Directly executing a batch through clicked function in qt

So I'm trying to have my "button" directly execute a Batch file, important here is that I don't want it to show me a dialogue and make me chose the path, which is the problem I'm having right now with the following code
void MainWindow::on_pushButton_clicked()
{
QString filename=QFileDialog::getOpenFileName(
this,
tr("Open File"),
"C://",
"All files (*.*);;Text File (*.txt);;Music file (*.mp3)");
}
I think this is probably really simple, but i can't get it, I'm not even learning c++ at the moment but my boss asked me to create something out of my scope (wants me to create a GUI for a batch file and have them interact) and I thought of this approach, which is just creating a GUI that executes it.
I've looked at this question: asked to execute an external program with Qt
but they don't talk about how the file path can directly be added into the code, or if I should even be using Qprocess and how, and if I can pass it through "clicked" function.
I'm really inexperienced, all of the code above I got with the help of the internet, but I really don't know how to program using c++
so could someone please be kind enough to show me how a file path can be added to the code, assuming it's in C:\Users\name_goes_here\Downloads
I'd really appreciate it :D
I'd recommend using QProcess for anything "execute external program" with Qt.
You could do it like this:
void MainWindow::on_pushButton_clicked()
{
QProcess process;
process.start("C:/Users/name_goes_here/Downloads/yourfile.bat");
process.waitForFinished(); // Assuming that you do want to wait for it to finish before the code execution resumes
}
Note the "/" in the path. Only Windows uses the messed up "\" for path separation, which would require you to write "C:\\Users\\.." in any string in C++ as "\" needs to be escaped.
Luckily, Qt uses "/" as the universal separator and translates it to whatever the OS needs as required. So you should just use "/" whenever working with Qt.
This is from the Qt documentation:
Qt uses "/" as a universal directory separator in the same way that "/" is used as a path separator in URLs. If you always use "/" as a directory separator, Qt will translate your paths to conform to the underlying operating system.
And finally, if you don't know how to code in C++, shouldn't you be learning that first instead of trying to execute batch files from within a library as complex as Qt? Sounds like you're trying to do too many new things at once.
This is fairly simple merging your source and the one you linked:
void MainWindow::on_pushButton_clicked()
{
QProcess::execute(
QString::fromLatin1(
"cmd.exe /c C:\\Users\\name_goes_here\\Downloads\\file.bat"));
}
Notes:
I used QProcess::execute() instead of QProcess::start() to make things even simpler.
To achieve execution of the batch file, I pass it to cmd32.exe as this is the interpreter which is responsible.
As MCVE testQProcessBatch.cc:
// Qt header:
#include <QtWidgets>
void on_pushButton_clicked()
{
#if 0 // WORKS:
QProcess::execute(
QString::fromUtf8("cmd.exe /c C:\\Users\\Scheff\\Downloads\\testBatch.bat"));
#else // WORKS AS WELL:
QProcess::execute(
QString::fromUtf8("C:\\Users\\Scheff\\Downloads\\testBatch.bat"));
#endif // 0
}
int main(int argc, char **argv)
{
qDebug() << "Version:" << QT_VERSION_STR;
// main application
QApplication app(argc, argv);
QMainWindow qWin;
QPushButton qBtn(QString::fromLatin1("Start cmd"));
qWin.setCentralWidget(&qBtn);
qWin.show();
QObject::connect(&qBtn, &QPushButton::clicked,
&on_pushButton_clicked);
// run application
return app.exec();
}
and the test batch file testBatch.bat:
echo "This is testBatch.bat"
pause
Tested with VS2013 on Windows 10:
Thanks for contributing guys!
I tried using the QProcess method but I think I'm too inexperienced when it comes to figuring out problems associated with it (which I did face when using this method). the CMD route is probably good but I also thought it was too difficult and both of these methods didn't work for me.
Here's what I have now (thanks to Detonar and ymoreau) and and it seems to be doing the job, this might not be the most optimal approach, but it worked for me!
I included QDesktopServices and QUrl
void MainWindow::on_pushButton_clicked()
{
QString filename="C:\\Users\\Name_goes_here\\Downloads\\test.bat";(
this);
hide(); //optional
QDesktopServices::openUrl(QUrl("file:///"+filename,QUrl::TolerantMode));
}

Redirecting the output of QProcess when running a resource file

fairly new to Qt.
I'm using QProcess to run an external shell script and redirecting the output to a textBrowser on my GUI. Code:
In mainwindow.h:
private:
QProcess *myProcess;
and mainwindow.cpp:
void MainWindow::onButtonPressed(){
myProcess = new QProcess(this);
myProcess->connect(myProcess, SIGNAL(readyRead()), this, SLOT(textAppend()));
myProcess->start("./someScript.sh", arguments);
}
void MainWindow::textAppend(){
ui->textBrowser->append(myProcess->readAll());
}
This works perfectly to run an external script. My question is how to apply the same process with the script included as a resource file.
I've tried simply replacing "./someScript.sh" with the resource version ":/someScript.sh" but it does not seem to work.
The resource script runs perfectly, but the console output disappears.
For this reason, there is something called "QTemporaryFile" class.
Because you need to call a file that already exists in your system - ok!
let's take this example :
using QProcess we need to run a python file from resource
//[1] Get Python File From Resource
QFile RsFile(":/send.py");
//[2] Create a Temporary File
QTemporaryFile *NewTempFile = QTemporaryFile::createNativeFile(RsFile);
//[3] Get The Path of Temporary File
QStringList arg;
arg << NewTempFile->fileName();
//[4] Call Process
QProcess *myProcess = new QProcess(this);
myProcess->start("python", arg);
//[5] When You Finish, remove the temporary file
NewTempFile->remove();
Note : on windows, the Temporary File stored in %TEMP% directory
and for more informations you can visit Qt Documentation - QTemporaryFile Class
Good Luck ♥
I does not work because when you run myProcess->start(":/someScript.sh", arguments); you ask your system to run :/someScript.sh which does not exist for your system.
A quick solution would be to copy the script to a temporary folder and run it from there.
QFile::copy(":/someScript.sh", pathToTmpFile);
myProcess->start(pathToTmpFile, arguments);
I would also suggest you make use of QTemporaryFile to get a unique temporary file name.

qt c++ QDialog open new file

After almost a week of searching and reading qt documentation, I still can't figure out how to use QDialog to create a NEW file on my hard disk for writing data. I can open a file and write data if the file already exists, but if I attempt to create a NEW file, I get a message that the file does not exist. I can create a new file if I do not use QDialog by hard coding the path and file name, but would like to be able to choose the file location, and get the customary messages; for instance that the file already exists and asked if it is OK to overwrite it. Here is a snippet of my latest attempt:
void MainWindow::on_pushButton_3_clicked()
{
QString filename = QFileDialog::getOpenFileName(
this,
tr("Sensor data"),
"C//",
"Text File (*.txt)"
);
QFile file(filename);
if (!file.open(QIODevice::ReadWrite))
{
QMessageBox::information(0,"info",file.errorString());
return;
}
QTextStream out(&file);
out<<"string1";
out<<"\n";
out<<"string2";
out<<"\n";
out<<"string3";
out<<"\n";
out<<"string4";
out<<"\n";
out<<"string5";
file.close();
}
Can QDialog be used for this purpose? If not, please point me to information on how it is done.
Thanks in advance!
I think you should use getSaveFileName instead
In Qt exemples there is project called SDI , content simple window and menu and all what you need about file : new, open, save, and save as .

Qt: check if a file in folder is changed

There any way to trigger an action if a file in a specified directory ( or in a subfolder ) without fetching all modification times every time ? I'm asking because i've to check this live
You need to use the QFileSystemWatcher.
More importantly, this is the signal you need to connect to:
void QFileSystemWatcher::fileChanged(const QString & path) [signal]
This signal is emitted when the file at the specified path is modified, renamed or removed from disk.
See also directoryChanged().
So, you could write something like this in your class or function:
...
QFileSystemWatcher watcher;
watcher.addPath("/My/Path/To/The/File");
QObject::connect(&watcher, SIGNAL(fileChanged(const QString&)), receiver, SLOT(handleFileChanged(const QString&)));
...
You're looking for QFileSystemWatcher.

QFiledialog returns the incorrect directory

A snippet of what i'm using looks like this
QDir lastDir;
QFileDialog dial(this);
dial.getOpenFileName(this,
tr("Open File"),
QString("/home"),
tr("Raw Images (*.nef *.NEF *.dng *.DNG)"));
lastDir = dial.directory();
qDebug() << lastDir;
The output, is completely wrong, no matter which directory I end up in. However, the incorrect directory is always the same.
AFAICT i'm doing nothing wrong here. What is going on here? Cheers
getOpenFileName() is a static function which immediately opens a "file picker" dialog and returns, once the user is finished with the dialog, "an existing file selected by the user". You use it like this (note the use of :: and the class name QFileDialog instead of the object name):
QString fileName = QFileDialog::getOpenFileName(this,
tr("Open File"),
QString("/home"),
tr("Raw Images (*.nef *.NEF *.dng *.DNG)"));
directory() is non-static and returns "the directory currently being displayed in the dialog". This function is meant to be called while the dialog is still open, it's intended for use cases which are not covered by the static calls.
What is happening here is you have instantiated an object, called a static function on it (which won't affect its state), and then called directory() which will just reflect the original state of the object, which is probably the working directory. Instead, you need to store the return value of the getOpenFileName() call in a variable, as shown above.
If you want to ask the user to just choose a directory, you could consider using getExistingDirectory() instead. Alternatively, if you want to extract the directory from the filename, the QDir class has some functions useful for this.