3

I have a Qt GUI-based full application but now I need to use this application in a sort of pipeline in batch mode (console). I've tried several approaches but none of them worked as expected. Here is what I have now:

QApplication a(argc, argv);
MyMainWindow *w = new MyMainWindow();
a.connect(&a, SIGNAL(lastWindowClosed()), &a, SLOT(quit()));
a.exec();

Here is what I need:

QApplication a(argc, argv);

QString project_path = argv[1];

MyMainWindow *w = new MyMainWindow();
a.connect(&a, SIGNAL(lastWindowClosed()), &a, SLOT(quit()));

w->loadPrjFile(project_path);
w->analyze();
w->exportResults();

a.exec();

So, basically what I need is to allow the application to get the project_path through command line (not file dialogs) and execute the methods that a user would usually follow using the GUI. The problem is that these methods should block, in the sense that one should wait for the previous one to finish.

The application itself should block, in the sense that, when executed in the shell, it should wait for the whole execution to finish before quitting. As it should work as a console application the interface should also be hidden.

If you know a way to do that, I would really appreciate a code sample of how this can be done.

  • Simple way is to use the various `waitForXxxx` methods of Qt (mostly `QIODevice` subclasses, and remember, they are bad idea in GUI apps), instead of ever calling `exec()`. Also `QFile` can be used on FILE pointer files like `stdin`, `stdout` and `stderr`. Or just use standard C++ streams etc, and convert to `QString` etc as needed. – hyde Dec 13 '13 at 19:23
  • To clarify my previous comment just in case: it's the `waitForXxx` methods which are a bad idea in GUI apps, because they block the event loop and lead to trouble in real use, and can be a lot of work to fix later. – hyde Dec 13 '13 at 19:38

3 Answers3

4

The problem you have is that you're trying to develop a console app, but still using Gui widgets, such as QMainWindow. You need to start by separating the Gui classes from everything else in your main project.

I recommend you create a class, derived from QObject, which handles the processing of what you need; loadPrjFile, analyze and exportResults.

Then use an instance of this new class in your MainWindow for the GUI project and use it directly for the console project.

class Worker : public QObject
{
    Q_OBJECT

    public:
        void loadPrjFile(const QString& path);
        void analyze();
        void exportResults();
};


class MyMainWindow : QMainWindow
{
    private:
        Worker m_pWorkerObject;
};

If you're developing a console project that doesn't need a Gui, you can use QCoreApplication, instead of QApplication.

Be aware that calling app.exec() starts Qt processing messages, so you only need that if you need a message loop to process events, which may not be the case for a console application, depending on what your app does.

TheDarkKnight
  • 27,181
  • 6
  • 55
  • 85
  • Your answer seems very professional, but I'm looking for a more straightfoward solution and I also don't know exactly how to do that as the interface and the logic are embarassingly mixed in the code I have at hand. – Rodrigo Ferreira Dec 14 '13 at 00:04
1

To read arguments from the command line in an app with a GUI, you can use the global pointer qApp anywhere in your code. This is particularly useful if you want, for example, to be able to associate your GUI application with a file type, since the file name will be pipleined by the OS to your app (at least it works in Windows).

You can see a detailed answer I gave to the same question in this thread, together with the links to the appropriate documentation, which for some reason is not in the latest versions of Qt.

The second part is not that easy.

You can use w->setVisible(false) before calling a.exec(); to hide your main window, but you will have, to the best of my knowledge, to modify every method that has a dialogue to either react to the command line argument if detected, and disable the dialogue, or use the normal dialogues if no related arguments are detected.

If you only need to call methods of the main window that have no interaction with the user, then it won't be that much work, and you might get away with not calling a.exec (if and only if no part of your code is using signals and slots in batch mode), which in reality starts the main loop of the GUI and won't be needed in that case.

Something like this might work:

QApplication a(argc, argv);
MyMainWindow *w = new MyMainWindow();


if(1 < qApp->arguments().count()) //Command line arguments detected
{
  QString project_path = qApp->arguments().at(1);

  w->loadPrjFile(project_path);
  w->analyze();
  w->exportResults();


}
else    //No command line arguments detected
{
  a.connect(&a, SIGNAL(lastWindowClosed()), &a, SLOT(quit()));
  a.exec();
}

If, on the other hand, the functions loadPrjFile(), analyze(); and exportResults() are slots and not, as your code suggests, methods of your main window, they will not be called sequentially and you will have no choice but using signals and slots so each function can notify the next one that it finished its job, and therefore will have to call a.exec

Community
  • 1
  • 1
  • I didn't accept your answer because it's in my opinion incomplete. It was very useful though. The main problems are the sequencial execution that you didn't mention in your answer and the a.exec() function that could not be taken out, at least in my specific case. – Rodrigo Ferreira Dec 14 '13 at 00:39
  • Thank you. I'm glad I could be of help. Because of the way you were calling your functions in sample.code, I assumed they were public methods instead of slots, and without the header of your main window I couldn't tell otherwise. Public methods are called sequentially and don't need a.exec unless they use events, as previously stated by @Merlin, but I edited my answer accordingly to make it clearer. About the use of qApp, you can move that exact same line to the slot that needs the file path, eliminating the need to pass it as an argument and keeping a neater main function – Dissident penguin Dec 15 '13 at 10:35
  • I also changed w-> hide() to w->setVisible(false), as suggested in your post. – Dissident penguin Dec 15 '13 at 10:52
1

This answer shows the solution that I came up with after a while. I'll put it here because it can be useful to others. The code looks like this:

QApplication a(argc, argv);

QString project_file = argv[1];

MyMainWindow *w = new MyMainWindow();
w->setVisible(false);
a.connect(&a, SIGNAL(lastWindowClosed()), &a, SLOT(quit()));
w->setBatchMode(true);

QObject::connect(w,SIGNAL(loadingFinished()),w,SLOT(analyze()));
QObject::connect(w,SIGNAL(analysisFinished()),w,SLOT(exportResults()));
QObject::connect(w,SIGNAL(exportingFinished()),w,SLOT(close()));

w->loadPrjFile(project_file);

a.exec();

The main considerations are:

  • w->setVisible(false) was used to hide the MainWindow as also pointed out by @Dissident penguin.

  • w->setBatchMode(true) was used to set a class variable that is used to supress all the other dialogs throughout the code as also pointed out by @Dissident penguin. Inside the functions I just wrapped the dialog code with an if statement like:

    if (!_batchMode) { //show dialog }

  • Meeting the requirement of sequencial execution was not that easy. I had to create two signals: loadingFinished(), analysisFinished() and exportingFinished(). Then I emit them in the end of loadPrjFile(), analyze() and exportResults() functions respectively. This way I garantee that they are executed in order and that one waits for the other. This is needed because slots are executed asynchronously in Qt.

  • Finally, I could not take out the method a.exec() because if I do that the program doesn't run properly. I think this is because I'm still using the GUI, it's just hidden. This way, a.exec() is still needed.