Qt startDetached fails under Qt 5.15.2 - c++

I'm updating some old Qt 5.6 code to Qt 5.15.2
The Qt C++ code below opens a dos prompt, the runs a bat file, staying open when done.
cstring = "cmd /k " +QDir::currentPath()+"/cpath.bat";
QProcess::startDetached(cstring);
This code works fine under 5.6, does nothing under 5.15.2
How do I fix this for Qt 5.15.2?

You need to split the command to program and arguments. See https://doc.qt.io/qt-5/qprocess.html#startDetached-1
In your case it should be
QProcess::startDetached("cmd", {"/k", QDir::currentPath() + "/cpath.bat"});
The advantage is that now you do not need to worry for example about enclosing paths containing spaces in quotes. Consider if your current path would be "C:\my path". Then your version from Qt 5.6 would not work. While the version I presented in this answer will work out of the box. I guess it was exactly this reason that the old overload was prone to this kind of bugs that Qt decided to remove it and use only the new overload.

Ok, with my previous answer I though that the problem was with the way you are passing the arguments. But as it seems the problem is with showing the terminal... Since I am doing the same in one of my projects (HiFile file manager), I had a look how I am doing it. So here it is.
class DetachableProcess : public QProcess
{
public:
explicit DetachableProcess(QObject *parent = nullptr) : QProcess(parent)
{
}
bool detach()
{
if (!waitForStarted())
{
return false;
}
setProcessState(QProcess::NotRunning);
return true;
}
};
And then use it like this:
DetachableProcess process;
process.setCreateProcessArgumentsModifier( // this probably did the trick you need
[](QProcess::CreateProcessArguments *args)
{
args->flags |= CREATE_NEW_CONSOLE;
args->startupInfo->dwFlags &=~ STARTF_USESTDHANDLES;
});
process.start("cmd", {"/k", QDir::currentPath() + "/cpath.bat"}); // here are your params...
process.detach();
I am afraid I cannot give more explanation why this complicated method works, I do not know it. And I do not know why it was changed in Qt.
I hope it will work for you. It works for me in Qt 6.2.4, which I am currently using.

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

Qt two QList member variables causes crash

so I'm not sure if this is a bug in Qt or if I just don't understand something, but i have this class:
class myClass : public QDialog, private Ui::myClass
{
Q_OBJECT
public:
explicit myClass(QWidget *parent = 0);
private:
QList<ushort> list1;
QList<ushort> list2;
}
I create this window by clicking on an action in another window:
void mainWindow::on_myClassAction_triggered()
{
myClass *mc = new mc(NULL);
mc->show();
}
So now things get weird. Even if i don't use list1 and list2 anywhere in myClass, the program will crash when i close or sometimes open myClass. If i comment out one, or both of them, it works. What is going on here??
I'm using Qt Creator. I just re-ran QMake and now it works. Definitely was some sort of bug within Qt/Qt Creator.
This is not a bug of any sort. It's a classic binary incompatibility problem: you've had some code that depended on the layout of some of your classes, but the outdated Makefiles did not capture that dependency. Thus when you changed the layout of the class, the dependent code didn't get recompiled. It would be way too expensive for qmake to rescan the entire project looking for such dependencies every time you build it. It's on you to re-run qmake when you change the code to introduce such binary incompatibilities.
For small projects, you may simply wish to always rebuild the code, forcing a qmake re-run.

Qt QFileDialog::setDirectory funny behavior in Ubuntu 12.04

I have a class that inherits from QFileDialog. In the constructor, I call setDirectory and pass in the last directory visited (which the class keeps track of; see code below). On Windows, this works fine. And if I show the dialog multiple times, it is internally smart enough to resume at the last location (e.g. where the user saved a file before). This is the desired behavior.
On Ubuntu 12.04 (GCC 4.8 compiler), on the other hand, the system does not automatically resume where last left off if I call showFileDialog multiple times. So I tried adding the setDirectory call within that function as commented below, but that didn't change anything. Furthermore, if I take out setDirectory from the constructor so it is only called in showFileDialog, the file dialog opens to the folder from which the program was run. (i.e. setDirectory didn't work.) Subsequent calls to showFileDialog will open a file dialog starting in the directory requested.
So it seems like the call has a delayed effectiveness. Is this a Qt bug, or mine? Either way, how can I get the setDirectory call to be effective?
Example code:
QString FileDialog::defaultDir = QDir::homePath();
FileDialog::FileDialog(QWidget *parentWindow /*, ...*/)
: QFileDialog(parentWindow)
{
setDirectory(defaultDir);
//...
}
QString FileDialog::showFileDialog()
{
// Adding setDirectory(defaultDir) here doesn't help.
if(!exec())
{
return QString::null;
}
defaultDir = directory().path();
//...
}
It is not clear from the code above how you know that the path was changed. I'm not sure that directory() is responsible for that.
Consider using void QFileDialog::directoryEntered(const QString & directory) signal.
Workaround found:
I happen to set the dialog title (setWindowTitle()) every time I open a FileDialog. If I connect to the QFileDialog::windowTitleChanged signal and call setDirectory within the slot, it is effective.
This is an unintuitive workaround though, so I am open to better answers.

Error from KGlobal::locale while opening a QFileDialog in C++

I've got this C++ code:
bool load ()
{
const QString * filename = openFileDialog(QFileDialog::AcceptOpen, QFileDialog::ExistingFile);
if (filename != 0)
return load(*filename);
else return false;
}
const QString * openFileDialog (QFileDialog::AcceptMode acceptMode, QFileDialog::FileMode mode)
{
QFileDialog dialog;
dialog.setAcceptMode(acceptMode);
dialog.setFileMode(mode);
dialog.show();
QStringList files = dialog.selectedFiles();
if (files.isEmpty()) return 0;
return new QString(files.at(1));
}
Now, I get the following error:
KGlobal::locale::Warning your global KLocale is being recreated with a valid main component instead of a fake component, this usually means you tried to call i18n related functions before your main component was created. You should not do that since it most likely will not work
The file dialog never appears.
I'm using Debian wheezy (german), KDE 4.8.4, Qt 4.8.2 and GCC 4.7.2
I'm late to the party, but this is apparently a very old issue anyway. It was solved 5 years ago, here: http://pyqt.riverbankcomputing.narkive.com/GMKOTskG/a-dire-warning-message#post16
The short answer:
When opening the file dialog, it's using the "system" file dialog. In this case, the system dialog is KDE, and an issue that is blamed on a KDE library produces that warning. Want to avoid the issue? Don't use the "system" file dialog.
In your openFileDialog function, simply add:
dialog.setOption(QFileDialog::DontUseNativeDialog)
This solution worked for me. (I'm using PyQt4, but was having the same symptoms.)

How to set auto=repeat on a qaction in a qtoolbar?

I'd like to use the autorepeat feature of the QToolButton class.
The problem is that the instances are created automatically when using QToolBar::addAction() and I can't find a way to reach them: QToolBar::widgetForAction() doesn't seem to work in that case (always returns NULL).
Any ideas?
Thanks
There seem to be no simple way. The best I found is to use QObject::findChldren :
foreach(QToolButton* pButton, pToolBar->findChildren<QToolButton*>()) {
if (pButton->defaultAction() == pTheActionIWant) {
...
}
}
In fact, in my case doesn't return NULL, maybe you are doing something different. My code is as follows:
QToolButton* button = dynamic_cast<QToolButton*>(
ui.toolBar->widgetForAction(ui.action));
For me it works as intended.... Maybe you aren't casting? This method returns a QWidget* and my compiler issues and error if I don't cast.
FYI, I'm using Visual Studio 2005 with Qt 4.6.