Saturday, February 16, 2013

QT Console Application Template Tutorial

Writing a command line program in QT may be a little harder than you would expect; particularly if you want to use the full features of QT including Signals, Slots and Threads.

If you start writing a QT program as a standard “main” program in “c” none of the QT messaging structure will operate as expected.

I have written many QT command line programs, some are commercial products.  Here is a template that I have developed which seems to work well.

Lets get started:

Here is the main.cpp file where it all starts.

#include <QtCore/QCoreApplication>
#include <QTimer>
#include "mainclass.h"
int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);


    // create the main class
    MainClass myMain;


    // connect up the signals
    QObject::connect(&myMain, SIGNAL(finished()), 

             &app, SLOT(quit()));
    QObject::connect(&app, SIGNAL(aboutToQuit()), 

             &myMain, SLOT(aboutToQuitApp()));

    // This code will start the messaging engine in QT and in
    // 10ms it will start the execution in the MainClass.run routine;
    QTimer::singleShot(10, &myMain, SLOT(run()));
    return app.exec();
}

 

A few things worth noting here.
  • A instance of the MainClass class called myMain is created.  
  • A signal in that MainClass called "finished" actually quits the application.
  • A signal from the app works with a slot in myMain called aboutToQuitApp
  • A 10ms timer sends a signal to the slot run in the myMain class.  This bootstraps your code.
  • The last line “return app.exec()” starts all of the QT messaging including the Slots and Signals system across various threads.
  • By the time myMain gets the signal on the “run” Slot the QT application structure is up and running.

Now lets create the header file for the MainClass;  mainclass.h

#ifndef MAINCLASS_H
#define MAINCLASS_H
#include <QObject>
#include <QCoreApplication>
class MainClass : public QObject
{
    Q_OBJECT
private:
    QCoreApplication *app;


public:
    explicit MainClass(QObject *parent = 0);
    /////////////////////////////////////////////////////////////
    /// Call this to quit application
    /////////////////////////////////////////////////////////////
    void quit();


signals:
    /////////////////////////////////////////////////////////////
    /// Signal to finish, this is connected to Application Quit
    /////////////////////////////////////////////////////////////
    void finished();


public slots:
    /////////////////////////////////////////////////////////////
    /// This is the slot that gets called from main to start everything
    /// but, everthing is set up in the Constructor
    /////////////////////////////////////////////////////////////
    void run();


    /////////////////////////////////////////////////////////////
    /// slot that get signal when that application is about to quit
    /////////////////////////////////////////////////////////////
    void aboutToQuitApp();
};
#endif // MAINCLASS_H


Lets look at a couple of things here.

  • Call “quit()” when you want to exit the application
  • The “finished” signal is sent to the app to close the application
  • The “run() slot is where your code “starts” gets called 10ms after the application is started
  • “aboutToQuitApp() slot gets called after the “finished” signal is executed by the QT application.

The last thing to look at is the actual mainclass.cpp code.

#include "mainclass.h"
#include <QDebug>
MainClass::MainClass(QObject *parent) :
    QObject(parent)
{
    // get the instance of the main application
    app = QCoreApplication::instance();
    // setup everything here
    // create any global objects
    // setup debug and warning mode
}


// 10ms after the application starts this method will run
// all QT messaging is running at this point so threads, signals and slots
// will all work as expected.
void MainClass::run()
{
    // Add your main code here
    qDebug() << "MainClass.Run is executing";
    // you must call quit when complete or the program will stay in the
    // messaging loop
    quit();
}


// call this routine to quit the application
void MainClass::quit()
{
    // you can do some cleanup here
    // then do emit finished to signal CoreApplication to quit
    emit finished();
}


// shortly after quit is called the CoreApplication will signal this routine
// this is a good place to delete any objects that were created in the
// constructor and/or to stop any threads
void MainClass::aboutToQuitApp()
{
    // stop threads
    // sleep(1);   // wait for threads to stop.
    // delete any objects
}


There are a few things to discuss here:

  • The constructor gets a instance of the QT application and sets it to “app”
  • The “run” slot is where your code will actually start execution.
  • When you are through running your code you must call “quit” to stop the application.  This will tell the QT application to terminate.
  • While the QT application is in the process of terminating it will execute the slot aboutToQuitApp().  This is a good place to do any clean-up work.

Well I hope this helps you get a QT Console application up and running quickly.

Comments are welcome.

7 comments:

  1. Nice blog post Trey! I am new to QT but enjoying the framework so far. I needed an example on how to write console applications in the tool.

    ReplyDelete
  2. Hi Trey, thanks for the perfect blog post! I just wonder why do I need the pointer to QCoreApplication in the MainClass (QCoreApplication *app)? Thanks

    ReplyDelete
  3. Very nice post. Very much appreciated!
    Thank you.

    ReplyDelete
  4. The first example of a Qt console application with event loop which exits cleanly. Extremely helpful, THANK YOU!

    ReplyDelete
  5. Trey, thanks for the example. One more advanced question, since you've used console apps with Qt in products - how do you handle user input in your console app? Do you spawn a separate thread that polls stdin periodically?

    I've got a TCP/IP socket server I'd like to dump the UI from and move it to a console GUI - but I don't want to use nCurses/PDCurses - just regular ol' console.

    Thanks!

    ReplyDelete