C++ Tutorial: Create QT applications without QTCreator

JasKinasis

Super Moderator
Staff member
Gold Supporter
Joined
Apr 25, 2017
Messages
1,851
Reaction score
2,695
Credits
15,957
Introduction:
In this thread user @blackneos940 asked a couple of QT related questions.

I've put together a little tutorial, which hopefully answers his questions.
But I've decided to post it in it's own thread, as a piece of stand-alone content for the rest of the community.

The questions essentially boiled down to:
1. How can I link a QT button to a terminal command?
2. Is it possible to create a QT based GUI application in the terminal without using QTCreator?

So the aim of this tutorial is to create a simple QT based GUI application from the command line without having to use QTCreator. We'll also address the button related question too. More on that in a bit.

For anybody who doesn't know:
QTCreator is an IDE (Integrated Development Environment) for QT. It is developed and maintained by the same people who develop the QT libraries and is an excellent piece of software to use for quickly building QT based applications.

Before trying to build QT programs from the command-line, I would personally recommend using QTCreator for your first few projects, until you have built up a bit more of a working knowledge of the QT application framework, its components and features.

Anyway, neo wanted to know if it is possible to create a QT GUI without QTCreator - it is - and this tutorial will show you how.
In order to answer neo's other question about connecting a button to a terminal command, we'll be developing a small QT Widgets based GUI application.
All we'll have in our main widget is:
- A button (using QT's QPushButton class)
- A text-browser (QTextBrowser).

When we click the button - we will start a terminal program "ls -alh $HOME/Desktop/" in a separate process and display its output in the text-browser.

In order to connect the button-click to a function that will start a terminal command. And to connect the output of the process to the text-browser, we will be using QT's signals and slots API's.


Pre-requisites:
At a minimum - this tutorial will require g++, make, qmake and the qtdeclarative5-dev package.

NOTE: I have pretty much all of the qt dev packages installed on my machine. But I'm fairly certain that the qtdeclarative5-dev package will bring in everything we need for this tutorial. All of the packages should be available via your distros usual package management tools.
Also, the exact name of the qt5 package you need might be sightly different on other distros. My listing is for debian based distros, so if you use a different distro, you may want to double check the package names.

Before we go any further - I will assume that you have all of the pre-requisites installed. Also, QT is primarily a C++ library, so the programming language we will be using will be C++. It is possible to create QT applications using python and various other languages, but this tutorial will be C++ based. So a basic understanding of C++ would be beneficial!

Creating the bare-bones of the application:
Rather than giving you all of the complete files in one go - we'll do this step by step as if we were starting from scratch.

So the first thing we'll do is set up our environment for our project, then we'll build the shell of our application and it's gui - with no functionality. Then we'll set up our build scripts and build our first version of our application. Then we'll add a few bits of functionality and build the final, definitive version.

So first, let's set up our environment for our project.
Open up a terminal and create a new empty directory called "NeosQt" for the source files to go into.
Bash:
mkdir NeosQt

Next, cd into the directory:
Bash:
cd NeosQt

And now let's fire up our favourite terminal-based text editor and create the main shell of our application. Personally, I use vim, but you can use whatever floats your boat!
Here's the listing for main.cpp:
C++:
#include <QtWidgets>
#include "mainwidget.h"

int main(int argc, char *argv[])
{
    // Creates an instance of QApplication
    QApplication a(argc, argv);

    // This is our MainWidget class containing our GUI and functionality
    MainWidget w;
    w.show(); // Show main window

    // run the application and return execs() return value/code
    return a.exec();
}

main.cpp is our applications main entry point. It is where our program starts and ends.
Incidentally, its code is also pretty much identical to what QTCreator would have auto-generated for us.

So what's going on in the above code?
We #inlude QtWidgets - which contains declarations of various QT widget classes including QApplication.
QApplication is a QT class which basically encapsulates all of the low-level stuff required for a QT application. It deals with initialising QT, displaying our GUI, resource management and our applications main events loop. For all intents and purposes it is a little black box.

We have also #include'd mainwidget.h - we haven't created that yet. We'll get onto that next - mainwidget.h will be the header for our main widget class. This will contain our GUI and all of the functionality for our little application.

At line 7 of the main() function, we create an instance of QApplication. As mentioned, this will deal with a lot of things for us.
At lines 10 and 11 - we create an instance of our MainWidget class and call it's show method.
Despite calling show() - our application is not actually ran until the final line is called:
Code:
return a.exec();
This executes our QApplication. It displays our mainwidget and sets the applications event-loop running so our application will respond to events and signals.

Our application will run until the user closes it down - at which point the return statement will return whatever value was returned by a.exec().
If a.exec() ends normally - it will return 0. Otherwise an error code will be returned.
In other words - Whatever value is returned by a.exec() is returned to the shell from main().

And that is about it for our main function. Not a huge amount to see and we won't need to see it again. That's done, completely in the bag. We won't need to revisit main.cpp again.

Next we'll declare our MainWidget class in mainwidget.h:
C++:
#ifndef MAINWIDGET_H
#define MAINWIDGET_H

#include <QWidget>

class QPushButton;
class QTextBrowser;

// This is the declaration of our MainWidget class
// The definition/implementation is in mainwidget.cpp
class MainWidget : public QWidget
{
    Q_OBJECT

public:
    explicit MainWidget(QWidget *parent = 0); //Constructor
    ~MainWidget(); // Destructor

private:
   QPushButton* button_;
   QTextBrowser* textBrowser_;
};

#endif // MAINWIDGET_H

Above, we can see our MainWidget class is publicly derived from QWidget. So our class is going to be a custom QT widget.

Skipping the pre-processor/header sections for now - we'll come back to those in a moment.
The first thing in our MainWidget class declaration is this:
Code:
Q_OBJECT
This is a pre-processor macro used by the QT build tools.
This lets the QT build tools know that our class is a QT object and will cause the build-tools to automatically create some additional intermediate code when we build our application later on.

In the "public:" section of our class - we only have a Constructor and Destructor.
As their names imply, the constructor is called when we create a new instance of our class and is used to allocate memory and initialise our widget.
The Destructor is called when we "destroy" an instance of our class. It is used to free up any dynamically allocated memory.

There are no other public functions in our class. So the only publicly available interfaces for our MainWidget class are its constructor and destructor. Which means the only thing that users of our class can do are create and destroy instances of it! The rest of its internals are private.

The next section "private:"
In here we have pointers to a QPushButton, and a QTextBrowser. That's all our application will initially contain - a button and a text-area.

Going back to the pre-processor section, at the top of the file we have a simple macro include guard (#ifndef XXX ... #define XXX ....#endif // XXX) which encapsulates our entire class declaration. This ensures that our header only gets included by the compiler once.

Our only #include is QWidget. We include it because our class is derived from QWidget - so the compiler will need to know exactly what QWidget is and how much memory to allocate for it.

At lines 6 and 7, we have two additional "class" declarations for QPushButton and QTextBrowser. These are called forward-declarations.

Now you may be wondering "Why didn't we #include the header files for QPushButton and QTextBrowser?".
The answer to that question is:
Because our class only stores pointers to a QPushButton and a QTextBrowser.
Pointers always have the same size - regardless of the size of the object they point to. So we can forward-declare the objects that they point to - which means that we will have to #include their headers in our implementation (.cpp) file.

So at this point, because we've used forward declarations - the compiler knows that our class contains two pointers to two different classes that will be fully defined later on. And the compiler already knows the size of a pointer - it already knows how much memory will be used by each of the pointers, even though it doesn't know anything about the classes themselves. So it will look for the complete definition of those classes in our implementation (.cpp) file.


Implementing our MainWidget class:
Next up we'll fully define our MainWidget class and set up its GUI.
First the code, then we'll take a look at it.

mainwidget.cpp:
C++:
#include <QtWidgets>
#include "mainwidget.h"

// Constructor for main widget
MainWidget::MainWidget(QWidget *parent) :
    QWidget(parent)
{
   button_ = new QPushButton(tr("Push Me!"));
   textBrowser_ = new QTextBrowser();

   QGridLayout *mainLayout = new QGridLayout;
   mainLayout->addWidget(button_,0,0);
   mainLayout->addWidget(textBrowser_,1,0);
   setLayout(mainLayout);
   setWindowTitle(tr("Connecting buttons to processes.."));
}

// Destructor
MainWidget::~MainWidget()
{
   delete button_;
   delete textBrowser_;
}

Analysis:
We #include QtWidgets - which has the delcarations for all of the QT widgets - e.g. Our QPushButton and QTextBrowser and the QGridLayout class we'll be using to help lay-out our application and the other QTClasses we'll be using later on.
Then we include the header for our MainWidget class. After all, this is MainWidget.cpp!

At the moment, we only have a constructor and a destructor in our class.
The constructor does rather a lot. In it's initialiser list, it first initialises all of its base members (QWidget), by passing the passed-in QWidget pointer (parent) into QWidgets constructor. This will set up and initialise all of the members for the QTWidget base class which our widget is derived from.

Then we create a new QPushButton and a new QTextBrowser. These are the two widgets/controls that will make up the entirety of our GUI.

Next we create a new QGridLayout object. This is the QT object which will deal with placing the controls on our widget. As it's name implies QGridLayout is a grid-based layout manager. There are several other types of layout managers available in QT - you can look them up in the QT documentation.

In our application we just want the button at the top and the text area underneath, So we've added our button to to the grid at position 0,0 (top left corner), and we added the textbox underneath it at position 1,0 (Bottom left corner). If we wanted them side by side, we could have put the textbox at 0,1. But for this example, I'm just going to put the textbox underneath the button.

Finally, the last thing we do in the constructor is set a window-title for our application.

Now let's take a look at the Destructor.
Our destructor deletes/free's the memory used by the dynamically allocated objects pointed to by our pointers (button_ and textBrowser_).

If we were using QTCreators form designer, it would have added some preprocessor macros, which in turn would add other additional bits of code which would automatically deal with memory management for the GUI elements. But because we have manually created our GUI, it is up to us to take care of memory management. So any new objects that our class creates will need to be deleted in the destructor.


And that is everything we need for the bare shell of our application - at least for now. Our GUI doesn't have any functionality yet. We'll add that later.


Before that, let's set up our build environment and build what we have so far.
So first, we'll use qmake to create a QT project file using the following command:
Bash:
qmake -project

This will create a .pro file. Typicaally it will be named after the parent directory contaning the source files.
So, assuming you called your initial directory NeosQt, the file will be called NeosQt.pro

The generated file should look something like this:
NeosQt.pro:
Code:
######################################################################
# Automatically generated by qmake (3.1) Wed Jun 20 21:44:00 2018
######################################################################

TEMPLATE = app
TARGET = NeosQt
INCLUDEPATH += .

# The following define makes your compiler warn you if you use any
# feature of Qt which has been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS

# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

# Input
HEADERS += mainwidget.h
SOURCES += main.cpp mainwidget.cpp

NOTE: The line "TARGET = NeosQt"
This will be the name of our final executable. By default qmake will use the name of the directory containing the source code to be the name of the executable.
You can change the target name if you want.

Before we can build our project - the next step will be to use qmake again and use our .pro file to generate a makefile.

But qmake is a bit rubbish at detecting/resolving dependencies sometimes. So before we move onto the next step we need to add an extra line to the .pro file.
We need to inform qmake which QT modules we'll be using. In this case, we're only using widgets, so we need to add the following line to our .pro file:
Code:
QT += widgets

So our .pro file should now look something like this:
Code:
######################################################################
# Automatically generated by qmake (3.1) Wed Jun 20 21:44:00 2018
######################################################################

TEMPLATE = app
TARGET = NeosQt
INCLUDEPATH += .

QT += widgets

# The following define makes your compiler warn you if you use any
# feature of Qt which has been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS

# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

# Input
HEADERS += mainwidget.h
SOURCES += main.cpp mainwidget.cpp

Now we can move onto the next step. Now we use a different qmake command which will use our .pro file to generate a makefile:
Bash:
qmake NeosQt.pro

This should have generated a Makefile.
So the final step should simply be:
Bash:
make

This will run gnu make and build our executable.

You can view the application by running:
Bash:
./NeosQt

And with any luck that should yield something that looks like this:
QTTutorialScreenshot.jpeg


So that is the shell of our GUI built. And we didn't use QTCreator! But our application doesn't do anything yet. So now we will add some functionality.

Adding functionality:
Now that we've created our basic application and our GUI - we want to make it do something when the button is pressed.

To facilitate this, we'll be using QT's "Signals and Slots" implementation.

So what are signals and slots?
Every QT widget/class has a set of "signals" that it can send/emit - the exact signals vary from class to class.

For example:
Widgets like buttons can send signals like "pressed" when a button is pressed, or "released" when it is released.

And slots are functions/methods that can be associated with signals - so when a signal is received - a slot function can be called. Some classes have pre-defined slots, but you can also create your own slot methods.

In order to connect a QT widgets signal to another classes slot method, you need to use QT's "connect" function.

The QT documentation is really good and lists any signals/slots that are available for any given class.

Getting back to our application. In order to make our MainWidget do something when we press the button, we need to associate one of our QPushButton's signals with a function/method in our MainWidget using QT's "connect" function/method.

In this case, we'll create a new slot function in our MainWidget class called onButtonReleased(). Then in MainWidget's constructor, we will use the connect function to associate/bind MainWidget's onButtonReleased() method with the buttons "released" signal.

If any of that doesn't make sense - hopefully the code will clear it up.

Speaking of clearing things up. We have a lot of new files that were generated when we last built our executable. So we'll do a little bit of housekeeping by running the following command to remove the executable we built and all of the intermediate files:
Bash:
rm NeosQt
make distclean
distclean in the above step will remove all of the intermediate files AND our makefile - but that's not a problem. We'll use qmake to regenerate it later.

After running the above commands, all we'll be left with will be our .h and .cpp files and our .pro file. And with that little bit of housekeeping out of the way, lets move on.

Our next task will be to update our mainwidget header to include the new onButtonReleased slot method... While we're there, we'll add a QProcess object, which we'll use to fire off a terminal command in the new method.

Here's the updated mainwidget.h:
C++:
#ifndef MAINWIDGET_H
#define MAINWIDGET_H

#include <QWidget>
#include <QProcess>

class QPushButton;
class QTextBrowser;

// This is the declaration of our MainWidget class
// The definition/implementation is in mainwidget.cpp
class MainWidget : public QWidget
{
    Q_OBJECT

public:
    explicit MainWidget(QWidget *parent = 0); //Constructor
    ~MainWidget(); // Destructor

private slots:
    void onButtonReleased(); // Handler for button presses

private:
   QPushButton* button_;
   QTextBrowser* textBrowser_;
   QProcess process_;
};

#endif // MAINWIDGET_H

NOTE: the "private slots" section is a QT specific section in the class declaration. This is where we declare QT slot methods that our class will use.
The "private" part of the "private slots" designation means that our slot function is a private member of the class - in other words it can only be used internally by our class. External classes/functions will not be able to access it.

Also, I decided that our class will use a QProcess object on the stack rather than dynamically on the heap. So because we aren't using a pointer, we need to include QProcess so the compiler will know how much space our class will need for its process_ member variable.

Now we'll fully define the new function in our .cpp file.
MainWidget.cpp:
C++:
#include <QtWidgets>
#include "mainwidget.h"

// Constructor for main window
MainWidget::MainWidget(QWidget *parent) :
    QWidget(parent)
{
   button_ = new QPushButton(tr("Push Me!"));
   textBrowser_ = new QTextBrowser();

   QGridLayout *mainLayout = new QGridLayout;
   mainLayout->addWidget(button_,0,0);
   mainLayout->addWidget(textBrowser_,1,0);
   setLayout(mainLayout);
   setWindowTitle(tr("Connecting buttons to processes.."));

   // Connect the Buttons "released" signal to MainWidget's onButtonReleased method.
   connect(button_, SIGNAL(released()), this, SLOT(onButtonReleased()));
}

// Destructor
MainWidget::~MainWidget()
{
   delete button_;
   delete textBrowser_;
}

// Handler for button click
void MainWidget::onButtonReleased()
{
    // clear the text in the textBrowser
    textBrowser_->clear();
    textBrowser_->append(tr("Running command:"));

    // Set up our process to write to stdout and run our command
    process_.setCurrentWriteChannel(QProcess::StandardOutput); // Set the write channel
    process_.start("ls -alh $HOME/Desktop"); // Start the program
}

OK, so what's new here? We've updated the constructor to connect our buttons "release" signal to MainWidgets onButtonReleased() slot method. We also added the onButtonReleased method, which sets up a QProcess and starts it running a terminal command - in this example we're setting it to run "ls -alh $HOME/desktop".

In the call to process_.setCurrentWriteChannel, we are setting stdout to be the processes default output stream. The call to process_.start starts the command running.

If we build and run the program now, we'll be able to press the button and it will run the command, but we have no way of knowing what the output of the command is, or whether it actually ran!

So our final task will be to find a way to connect the output from our process to our QTextBrowser object (textBrowser_).
Once we've done that, our application will be complete.

We've already seen signals and slots in action when we connected our button to a handler function. Now we're going to do exactly the same thing again. But this time, we are going to connect a signal from QProcess to a method that will get the output from the process and append it to our text-browser.

So first up we'll need to add a declaration for new private slot function in mainwidget.h:
C++:
#ifndef MAINWIDGET_H
#define MAINWIDGET_H

#include <QWidget>
#include <QProcess>

class QPushButton;
class QTextBrowser;

// This is the declaration of our MainWidget class
// The definition/implementation is in mainwidget.cpp
class MainWidget : public QWidget
{
    Q_OBJECT

public:
    explicit MainWidget(QWidget *parent = 0); //Constructor
    ~MainWidget(); // Destructor

private slots:
    void onButtonReleased(); // Handler for button presses
    void onCaptureProcessOutput(); // Handler for Process output

private:
   QPushButton* button_;
   QTextBrowser* textBrowser_;
   QProcess process_;   // This is the process the button will fire off
};

#endif // MAINWIDGET_H

The next part is to connect the Process to the new handler function and to fully define the new handler. So we'll be using signals and slots to connect the QProcess to the new handler function/method, but we'll be listening for a differrent signal. As mentioned previously, different QT classes emit different signals.
Again, the QT documentation lists each classes signals and any built-in slot functions they might have.
The QProcess signal we will be listening for will be the readyReadStandardOutput() signal.
When we receive the readyReadStandardOutput() signal, our new onCaptureProcessOutput() method will be called and in turn, it will append whatever text it receives to the text in our text-browser.

Here's the final code for mainwidget.cpp - we'll discuss what happens in the new onCaptureProcessOutput method afterwards:
C++:
#include <QtWidgets>
#include "mainwidget.h"

// Constructor for main window
MainWidget::MainWidget(QWidget *parent) :
    QWidget(parent)
{
   button_ = new QPushButton(tr("Push Me!"));
   textBrowser_ = new QTextBrowser();

   QGridLayout *mainLayout = new QGridLayout;
   mainLayout->addWidget(button_,0,0);
   mainLayout->addWidget(textBrowser_,1,0);
   setLayout(mainLayout);
   setWindowTitle(tr("Connecting buttons to processes.."));

   connect(button_, SIGNAL(released()), this, SLOT(onButtonReleased()));
   connect(&process_, SIGNAL(readyReadStandardOutput()), this, SLOT(onCaptureProcessOutput()));
}

// Destructor
MainWidget::~MainWidget()
{
    delete button_;
   delete textBrowser_;
}

// Handler for button click
void MainWidget::onButtonReleased()
{
    // clear the text in the textBrowser and start the process
    textBrowser_->clear();

    // Set up our process to write to stdout and run our command
    process_.setCurrentWriteChannel(QProcess::StandardOutput); // Set the write channel
   process_.start("ls -alh $HOME/desktop"); // Start a terminal command
}


// This is called whenever the QProcess::readyReadStandardOutput() signal is received
void MainWidget::onCaptureProcessOutput()
{
   // Determine whether the object that sent the signal was a pointer to a process
   QProcess* process = qobject_cast<QProcess*>(sender());
   // If so, append the output to the textbrowser
   if (process)
       textBrowser_->append(process->readAllStandardOutput());
}

At first glance, what is happening inside the onCaptureProcessOutput method probably looks like some kind of black magic. But it is actually quite simple.

What it is doing is calling the sender() function to find out which class sent us the readyReadStandardOutput() signal. Sender returns a pointer to the QT class that sent us the signal.
So we need to check what type of object it was that sent us the signal.
To do this, we use qobject_cast to try and cast the pointer to a QProcess pointer.

If the qobject_cast succeeds and we manage to get a valid QProcess pointer, we know that the object that sent us the signal was a QProcess. And if it fails - we'll get a NULL pointer and we'll know it was some other widget/class that sent it, so we ignore it.

If we successfully managed to cast the sender pointer to a QProcess, we then get all of the standard output from the process and append it into our text-browser.

And that is the functionality complete for our little example application.

The final step is to re-build the application and check it all works.
If you remember - earlier on, we used "make distclean" to clear out all of the intermediate files. But it also removed our makefile.
So the next step is to re-generate our makefile using qmake:
Bash:
qmake NeosQt.pro

Then it's just a case of running make, waiting for the executable to build and link and then test the program.
All being well, the application will look exactly as it did before, but this time - when you click the button, the ls command will be invoked and you should see some text appear in the text box.


ROUND-UP:
I think this tutorial meets all of @blackneos940 's requirements.
We have hand-coded a QT GUI from scratch using one of QT's layout manager classes.
We have used signals and slots to start a process using a button.
We also used signals and slots to connect the output of the process to a text-browser.
And we used nothing more than a text editor and a terminal.

I haven't had much time to work on this tutorial - some of it was a bit rushed - so it may get some subsequent updates/edits in coming days/weeks/months/years! XD

Also, the code used in this tutorial isn't completely bulletproof. There are a number of things that could be done slightly differently to make the application a bit more robust, but I was just trying to keep things as simple as possible for the sake of illustrating how to create a simple Widget based application in Qt without using QTCreator.

If anybody has any questions or comments, please leave them below.
 
Last edited:


Introduction:
In this thread user @blackneos940 asked a couple of QT related questions.

I've put together a little tutorial, which hopefully answers his questions.
But I've decided to post it in it's own thread, as a piece of stand-alone content for the rest of the community.

The questions essentially boiled down to:
1. How can I link a QT button to a terminal command?
2. Is it possible to create a QT based GUI application in the terminal without using QTCreator?

So the aim of this tutorial is to create a simple QT based GUI application from the command line without having to use QTCreator. We'll also address the button related question too. More on that in a bit.

For anybody who doesn't know:
QTCreator is an IDE (Integrated Development Environment) for QT. It is developed and maintained by the same people who develop the QT libraries and is an excellent piece of software to use for quickly building QT based applications.

Before trying to build QT programs from the command-line, I would personally recommend using QTCreator for your first few projects, until you have built up a bit more of a working knowledge of the QT application framework, its components and features.

Anyway, neo wanted to know if it is possible to create a QT GUI without QTCreator - it is - and this tutorial will show you how.
In order to answer neo's other question about connecting a button to a terminal command, we'll be developing a small QT Widgets based GUI application.
All we'll have in our main widget is:
- A button (using QT's QPushButton class)
- A text-browser (QTextBrowser).

When we click the button - we will start a terminal program "ls -alh $HOME/Desktop/" in a separate process and display its output in the text-browser.

In order to connect the button-click to a function that will start a terminal command. And to connect the output of the process to the text-browser, we will be using QT's signals and slots API's.


Pre-requisites:
At a minimum - this tutorial will require g++, make, qmake and the qtdeclarative5-dev package.

NOTE: I have pretty much all of the qt dev packages installed on my machine. But I'm fairly certain that the qtdeclarative5-dev package will bring in everything we need for this tutorial. All of the packages should be available via your distros usual package management tools.
Also, the exact name of the qt5 package you need might be sightly different on other distros. My listing is for debian based distros, so if you use a different distro, you may want to double check the package names.

Before we go any further - I will assume that you have all of the pre-requisites installed. Also, QT is primarily a C++ library, so the programming language we will be using will be C++. It is possible to create QT applications using python and various other languages, but this tutorial will be C++ based. So a basic understanding of C++ would be beneficial!

Creating the bare-bones of the application:
Rather than giving you all of the complete files in one go - we'll do this step by step as if we were starting from scratch.

So the first thing we'll do is set up our environment for our project, then we'll build the shell of our application and it's gui - with no functionality. Then we'll set up our build scripts and build our first version of our application. Then we'll add a few bits of functionality and build the final, definitive version.

So first, let's set up our environment for our project.
Open up a terminal and create a new empty directory called "NeosQt" for the source files to go into.
Code:
mkdir NeosQt

Next, cd into the directory:
Code:
cd NeosQt

And now let's fire up our favourite terminal-based text editor and create the main shell of our application. Personally, I use vim, but you can use whatever floats your boat!
Here's the listing for main.cpp:
Code:
#include <QtWidgets>
#include "mainwidget.h"

int main(int argc, char *argv[])
{
    // Creates an instance of QApplication
    QApplication a(argc, argv);

    // This is our MainWidget class containing our GUI and functionality
    MainWidget w;
    w.show(); // Show main window

    // run the application and return execs() return value/code
    return a.exec();
}

main.cpp is our applications main entry point. It is where our program starts and ends.
Incidentally, its code is also pretty much identical to what QTCreator would have auto-generated for us.

So what's going on in the above code?
We #inlude QtWidgets - which contains declarations of various QT widget classes including QApplication.
QApplication is a QT class which basically encapsulates all of the low-level stuff required for a QT application. It deals with initialising QT, displaying our GUI, resource management and our applications main events loop. For all intents and purposes it is a little black box.

We have also #include'd mainwidget.h - we haven't created that yet. We'll get onto that next - mainwidget.h will be the header for our main widget class. This will contain our GUI and all of the functionality for our little application.

At line 7 of the main() function, we create an instance of QApplication. As mentioned, this will deal with a lot of things for us.
At lines 10 and 11 - we create an instance of our MainWidget class and call it's show method.
Despite calling show() - our application is not actually ran until the final line is called:
Code:
return a.exec();
This executes our QApplication. It displays our mainwidget and sets the applications event-loop running so our application will respond to events and signals.

Our application will run until the user closes it down - at which point the return statement will return whatever value was returned by a.exec().
If a.exec() ends normally - it will return 0. Otherwise an error code will be returned.
In other words - Whatever value is returned by a.exec() is returned to the shell from main().

And that is about it for our main function. Not a huge amount to see and we won't need to see it again. That's done, completely in the bag. We won't need to revisit main.cpp again.

Next we'll declare our MainWidget class in mainwidget.h:
Code:
#ifndef MAINWIDGET_H
#define MAINWIDGET_H

#include <QWidget>

class QPushButton;
class QTextBrowser;

// This is the declaration of our MainWidget class
// The definition/implementation is in mainwidget.cpp
class MainWidget : public QWidget
{
    Q_OBJECT

public:
    explicit MainWidget(QWidget *parent = 0); //Constructor
    ~MainWidget(); // Destructor

private:
   QPushButton* button_;
   QTextBrowser* textBrowser_;
};

#endif // MAINWIDGET_H

Above, we can see our MainWidget class is publicly derived from QWidget. So our class is going to be a custom QT widget.

Skipping the pre-processor/header sections for now - we'll come back to those in a moment.
The first thing in our MainWidget class declaration is this:
Code:
Q_OBJECT
This is a pre-processor macro used by the QT build tools.
This lets the QT build tools know that our class is a QT object and will cause the build-tools to automatically create some additional intermediate code when we build our application later on.

In the "public:" section of our class - we only have a Constructor and Destructor.
As their names imply, the constructor is called when we create a new instance of our class and is used to allocate memory and initialise our widget.
The Destructor is called when we "destroy" an instance of our class. It is used to free up any dynamically allocated memory.

There are no other public functions in our class. So the only publicly available interfaces for our MainWidget class are its constructor and destructor. Which means the only thing that users of our class can do are create and destroy instances of it! The rest of its internals are private.

The next section "private:"
In here we have pointers to a QPushButton, and a QTextBrowser. That's all our application will initially contain - a button and a text-area.

Going back to the pre-processor section, at the top of the file we have a simple macro include guard (#ifndef XXX ... #define XXX ....#endif // XXX) which encapsulates our entire class declaration. This ensures that our header only gets included by the compiler once.

Our only #include is QWidget. We include it because our class is derived from QWidget - so the compiler will need to know exactly what QWidget is and how much memory to allocate for it.

At lines 6 and 7, we have two additional "class" declarations for QPushButton and QTextBrowser. These are called forward-declarations.

Now you may be wondering "Why didn't we #include the header files for QPushButton and QTextBrowser?".
The answer to that question is:
Because our class only stores pointers to a QPushButton and a QTextBrowser.
Pointers always have the same size - regardless of the size of the object they point to. So we can forward-declare the objects that they point to - which means that we will have to #include their headers in our implementation (.cpp) file.

So at this point, because we've used forward declarations - the compiler knows that our class contains two pointers to two different classes that will be fully defined later on. And the compiler already knows the size of a pointer - it already knows how much memory will be used by each of the pointers, even though it doesn't know anything about the classes themselves. So it will look for the complete definition of those classes in our implementation (.cpp) file.


Implementing our MainWidget class:
Next up we'll fully define our MainWidget class and set up its GUI.
Firts the code, then we'll take a look at it.

mainwidget.cpp:
Code:
#include <QtWidgets>
#include "mainwidget.h"

// Constructor for main widget
MainWidget::MainWidget(QWidget *parent) :
    QWidget(parent)
{
   button_ = new QPushButton(tr("Push Me!"));
   textBrowser_ = new QTextBrowser();

   QGridLayout *mainLayout = new QGridLayout;
   mainLayout->addWidget(button_,0,0);
   mainLayout->addWidget(textBrowser_,1,0);
   setLayout(mainLayout);
   setWindowTitle(tr("Connecting buttons to processes.."));
}

// Destructor
MainWidget::~MainWidget()
{
   delete button_;
   delete textBrowser_;
}

Analysis:
We #include QtWidgets - which has the delcarations for all of the QT widgets - e.g. Our QPushButton and QTextBrowser and the QGridLayout class we'll be using to help lay-out our application and the other QTClasses we'll be using later on.
Then we include the header for our MainWidget class. After all, this is MainWidget.cpp!

At the moment, we only have a constructor and a destructor in our class.
The constructor does rather a lot. In it's initialiser list, it first initialises all of its base members (QWidget), by passing the passed-in QWidget pointer (parent) into QWidgets constructor. This will set up and initialise all of the members for the QTWidget base class which our widget is derived from.

Then we create a new QPushButton and a new QTextBrowser. These are the two widgets/controls that will make up the entirety of our GUI.

Next we create a new QGridLayout object. This is the QT object which will deal with placing the controls on our widget. As it's name implies QGridLayout is a grid-based layout manager. There are several other types of layout managers available in QT - you can look them up in the QT documentation.

In our application we just want the button at the top and the text area underneath, So we've added our button to to the grid at position 0,0 (top left corner), and we added the textbox underneath it at position 1,0 (Bottom left corner). If we wanted them side by side, we could have put the textbox at 0,1. But for this example, I'm just going to put the textbox underneath the button.

Finally, the last thing we do in the constructor is set a window-title for our application.

Now let's take a look at the Destructor.
Our destructor deletes/free's the memory used by the dynamically allocated objects pointed to by our pointers (button_ and textBrowser_).

If we were using QTCreators form designer, it would have added some preprocessor macros, which in turn would add other additional bits of code which would automatically deal with memory management for the GUI elements. But because we have manually created our GUI, it is up to us to take care of memory management. So any new objects that our class creates will need to be deleted in the destructor.


And that is everything we need for the bare shell of our application - at least for now. Our GUI doesn't have any functionality yet. We'll add that later.


Before that, let's set up our build environment and build what we have so far.
So first, we'll use qmake to create a QT project file using the following command:
Code:
qmake -project

This will create a .pro file. Typicaally it will be named after the parent directory contaning the source files.
So, assuming you called your initial directory NeosQt, the file will be called NeosQt.pro

The generated file should look something like this:
NeosQt.pro:
Code:
######################################################################
# Automatically generated by qmake (3.1) Wed Jun 20 21:44:00 2018
######################################################################

TEMPLATE = app
TARGET = NeosQt
INCLUDEPATH += .

# The following define makes your compiler warn you if you use any
# feature of Qt which has been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS

# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

# Input
HEADERS += mainwidget.h
SOURCES += main.cpp mainwidget.cpp

NOTE: The line "TARGET = NeosQt"
This will be the name of our final executable. By default qmake will use the name of the directory containing the source code to be the name of the executable.
You can change the target name if you want.

Before we can build our project - the next step will be to use qmake again and use our .pro file to generate a makefile.

But qmake is a bit rubbish at detecting/resolveing dependencies sometimes. So before we move onto the next step we need to add an extra line to the .pro file.
We need to inform qmake which QT modules we'll be using. In this case, we're only using widgets, so we need to add the following line to our .pro file:
Code:
QT += widgets

So our .pro file should now look something like this:
Code:
######################################################################
# Automatically generated by qmake (3.1) Wed Jun 20 21:44:00 2018
######################################################################

TEMPLATE = app
TARGET = NeosQt
INCLUDEPATH += .

QT += widgets

# The following define makes your compiler warn you if you use any
# feature of Qt which has been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS

# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

# Input
HEADERS += mainwidget.h
SOURCES += main.cpp mainwidget.cpp

Now we can move onto the next step. Now we use a different qmake command which will use our .pro file to generate a makefile:
Code:
qmake NeosQt.pro

This should have generated a Makefile.
So the final step should simply be:
Code:
make

This will run gnu make and build our executable.

You can view the application by running:
Code:
./NeosQt

And with any luck that should yield something that looks like this:
View attachment 3176

So that is the shell of our GUI built. And we didn't use QTCreator! But our application doesn't do anything yet. So now we will add some functionality.

Adding functionality:
Now that we've created our basic application and our GUI - we want to make it do something when the button is pressed.

To facilitate this, we'll be using QT's "Signals and Slots" implementation.

So what are signals and slots?
Every QT widget/class has a set of "signals" that it can send/emit - the exact signals vary from class to class.

For example:
Widgets like buttons can send signals like "pressed" when a button is pressed, or "released" when it is released.

And slots are functions/methods that can be associated with signals - so when a signal is received - a slot function can be called. Some classes have pre-defined slots, but you can also create your own slot methods.

In order to connect a QT widgets signal to another classes slot method, you need to use QT's "connect" function.

The QT documentation is really good and lists any signals/slots that are available for any given class.

Getting back to our application. In order to make our MainWidget do something when we press the button, we need to associate one of our QPushButton's signals with a function/method in our MainWidget using QT's "connect" function/method.

In this case, we'll create a new slot function in our MainWidget class called onButtonReleased(). Then in MainWidget's constructor, we will use the connect function to associate/bind MainWidget's onButtonReleased() method with the buttons "released" signal.

If any of that doesn't make sense - hopefully the code will clear it up.

Speaking of clearing things up. We have a lot of new files that were generated when we last built our executable. So we'll do a little bit of housekeeping by running the following command to remove the executable we built and all of the intermediate files:
Code:
rm NeosQt
make distclean
distclean in the above step will remove all of the intermediate files AND our makefile - but that's not a problem. We'll use qmake to regenerate it later.

After running the above commands, all we'll be left with will be our .h and .cpp files and our .pro file. And with that little bit of housekeeping out of the way, lets move on.

Our next task will be to update our mainwidget header to include the new onButtonReleased slot method... While we're there, we'll add a QProcess object, which we'll use to fire off a terminal command in the new method.

Here's the updated mainwidget.h:
Code:
#ifndef MAINWIDGET_H
#define MAINWIDGET_H

#include <QWidget>
#include <QProcess>

class QPushButton;
class QTextBrowser;

// This is the declaration of our MainWidget class
// The definition/implementation is in mainwidget.cpp
class MainWidget : public QWidget
{
    Q_OBJECT

public:
    explicit MainWidget(QWidget *parent = 0); //Constructor
    ~MainWidget(); // Destructor

private slots:
    void onButtonReleased(); // Handler for button presses

private:
   QPushButton* button_;
   QTextBrowser* textBrowser_;
   QProcess process_;
};

#endif // MAINWIDGET_H

NOTE: the "private slots" section is a QT specific section in the class declaration. This is where we declare QT slot methods that our class will use.
The "private" part of the "private slots" designation means that our slot function is a private member of the class - in other words it can only be used internally by our class. External classes/functions will not be able to access it.

Also, I decided that our class will use a QProcess object on the stack rather than dynamically on the heap. So because we aren't using a pointer, we need to include QProcess so the compiler will know how much space our class will need for its process_ member variable.

Now we'll fully define the new function in our .cpp file.
MainWidget.cpp:
Code:
#include <QtWidgets>
#include "mainwidget.h"

// Constructor for main window
MainWidget::MainWidget(QWidget *parent) :
    QWidget(parent)
{
   button_ = new QPushButton(tr("Push Me!"));
   textBrowser_ = new QTextBrowser();

   QGridLayout *mainLayout = new QGridLayout;
   mainLayout->addWidget(button_,0,0);
   mainLayout->addWidget(textBrowser_,1,0);
   setLayout(mainLayout);
   setWindowTitle(tr("Connecting buttons to processes.."));

   // Connect the Buttons "released" signal to MainWidget's onButtonReleased method.
   connect(button_, SIGNAL(released()), this, SLOT(onButtonReleased()));
}

// Destructor
MainWidget::~MainWidget()
{
   delete button_;
   delete textBrowser_;
}

// Handler for button click
void MainWidget::onButtonReleased()
{
    // clear the text in the textBrowser
    textBrowser_->clear();
    textBrowser_->append(tr("Running command:"));

    // Set up our process to write to stdout and run our command
    process_.setCurrentWriteChannel(QProcess::StandardOutput); // Set the write channel
    process_.start("ls -alh $HOME/Desktop"); // Start the program
}

OK, so what's new here? We've updated the constructor to connect our buttons "release" signal to MainWidgets onButtonReleased() slot method. We also added the onButtonReleased method, which sets up a QProcess and starts it running a terminal command - in this example we're setting it to run "ls -alh $HOME/desktop".

In the call to process_.setCurrentWriteChannel, we are setting stdout to be the processes default output stream. The call to process_.start starts the command running.

If we build and run the program now, we'll be able to press the button and it will run the command, but we have no way of knowing what the output of the command is, or whether it actually ran!

So our final task will be to find a way to connect the output from our process to our QTextBrowser object (textBrowser_).
Once we've done that, our application will be complete.

We've already seen signals and slots in action when we connected our button to a handler function. Now we're going to do exactly the same thing again. But this time, we are going to connect a signal from QProcess to a method that will get the output from the process and append it to our text-browser.

So first up we'll need to add a declaration for new private slot function in mainwidget.h:
Code:
#ifndef MAINWIDGET_H
#define MAINWIDGET_H

#include <QWidget>
#include <QProcess>

class QPushButton;
class QTextBrowser;

// This is the declaration of our MainWidget class
// The definition/implementation is in mainwidget.cpp
class MainWidget : public QWidget
{
    Q_OBJECT

public:
    explicit MainWidget(QWidget *parent = 0); //Constructor
    ~MainWidget(); // Destructor

private slots:
    void onButtonReleased(); // Handler for button presses
    void onCaptureProcessOutput(); // Handler for Process output

private:
   QPushButton* button_;
   QTextBrowser* textBrowser_;
   QProcess process_;   // This is the process the button will fire off
};

#endif // MAINWIDGET_H

The next part is to connect the Process to the new handler function and to fully define the new handler. So we'll be using signals and slots to connect the QProcess to the new handler function/method, but we'll be listening for a differrent signal. As mentioned previously, different QT classes emit different signals.
Again, the QT documentation lists each classes signals and any built-in slot functions they might have.
The QProcess signal we will be listening for will be the readyReadStandardOutput() signal.
When we receive the readyReadStandardOutput() signal, our new onCaptureProcessOutput() method will be called and in turn, it will append whatever text it receives to the text in our text-browser.

Here's the final code for mainwidget.cpp - we'll discuss what happens in the new onCaptureProcessOutput method afterwards:
Code:
#include <QtWidgets>
#include "mainwidget.h"

// Constructor for main window
MainWidget::MainWidget(QWidget *parent) :
    QWidget(parent)
{
   button_ = new QPushButton(tr("Push Me!"));
   textBrowser_ = new QTextBrowser();

   QGridLayout *mainLayout = new QGridLayout;
   mainLayout->addWidget(button_,0,0);
   mainLayout->addWidget(textBrowser_,1,0);
   setLayout(mainLayout);
   setWindowTitle(tr("Connecting buttons to processes.."));

   connect(button_, SIGNAL(released()), this, SLOT(onButtonReleased()));
   connect(&process_, SIGNAL(readyReadStandardOutput()), this, SLOT(onCaptureProcessOutput()));
}

// Destructor
MainWidget::~MainWidget()
{
    delete button_;
   delete textBrowser_;
}

// Handler for button click
void MainWidget::onButtonReleased()
{
    // clear the text in the textBrowser and start the process
    textBrowser_->clear();

    // Set up our process to write to stdout and run our command
    process_.setCurrentWriteChannel(QProcess::StandardOutput); // Set the write channel
   process_.start("ls -alh $HOME/desktop"); // Start a terminal command
}


// This is called whenever the QProcess::readyReadStandardOutput() signal is received
void MainWidget::onCaptureProcessOutput()
{
   // Determine whether the object that sent the signal was a pointer to a process
   QProcess* process = qobject_cast<QProcess*>(sender());
   // If so, append the output to the textbrowser
   if (process)
       textBrowser_->append(process->readAllStandardOutput());
}

At first glance, what is happening inside the onCaptureProcessOutput method probably looks like some kind of black magic. But it is actually quite simple.

What it is doing is calling the sender() function to find out which class sent us the readyReadStandardOutput() signal. Sender returns a pointer to the QT class that sent us the signal.
So we need to check what type of object it was that sent us the signal.
To do this, we use qobject_cast to try and cast the pointer to a QProcess pointer.

If the qobject_cast succeeds and we manage to get a valid QProcess pointer, we know that the object that sent us the signal was a QProcess. And if it fails - we'll get a NULL pointer and we'll know it was some other widget/class that sent it, so we ignore it.

If we successfully managed to cast the sender pointer to a QProcess, we then get all of the standard output from the process and append it into our text-browser.

And that is the functionality complete for our little example application.

The final step is to re-build the application and check it all works.
If you remember - earlier on, we used "make distclean" to clear out all of the intermediate files. But it also removed our makefile.
So the next step is to re-generate our makefile using qmake:
Code:
qmake NeosQt.pro

Then it's just a case of running make, waiting for the executable to build and link and then test the program.
All being well, the application will look exactly as it did before, but this time - when you click the button, the ls command will be invoked and you should see some text appear in the text box.


ROUND-UP:
I think this tutorial meets all of @blackneos940 's requirements.
We have hand-coded a QT GUI from scratch using one of QT's layout manager classes.
We have used signals and slots to start a process using a button.
We also used signals and slots to connect the output of the process to a text-browser.
And we used nothing more than a text editor and a terminal.

I haven't had much time to work on this tutorial - some of it was a bit rushed - so it may get some subsequent updates/edits in coming days/weeks/months/years! XD

Also, the code used in this tutorial isn't completely bulletproof. There are a number of things that could be done slightly differently to make the application a bit more robust, but I was just trying to keep things as simple as possible for the sake of illustrating how to create a simple Widget based application in Qt without using QTCreator.

If anybody has any questions or comments, please leave them below.
WHOA!!..... :O We need a LOVE button on this Site!!..... :D Thank you SO MUCH, Jas..... :3 I'll read this, and read it well..... :3 Again, THANK YOU..... :3
 
This is very good! I was looking for this kind of tutorial as well. I'm new to Qt but it's good to have this kind of resource.

I dig it further for qt

  • qobject_cast() uses static_cast() as final step thus it doesn't rely on RTTI. It's template function that depends on input object, just mostly to provide type of object matching check for us.
  • Qt generally has macros defined in its header to accommodate its pre-processing used by tool like moc to generate meta info. Things like slots will be empty in normal compilation step, but not when it's done with moc tool.
Thanks again for this writeup!
 
WHOA!!..... :O We need a LOVE button on this Site!!..... :D Thank you SO MUCH, Jas..... :3 I'll read this, and read it well..... :3 Again, THANK YOU..... :3
There is: place cursor on the like button, don't click it and wait, some options will appear, among those it's love. I just did! :)

@JasKinasis great tutorial man! :)
 
@JasKinasis thanks so much for this tutorial! I've been looking everywhere for something minimalist like this!

In ran into some issues on the first compile so I'll leave some tips that may be useful for other users:

1 - My compiler was throwing errors like
Code:
error: invalid use of incomplete type ‘class QPushButton’
and the same for QTextBrowser and QGridLayout. I solved it by#includeing them in both mainwidget.h and mainwidget.cpp;

2 - I put my .h files in headers/ and I was having an error on compilation:
Code:
fatal error: mainwidget.h: No such file or directory
even though in the .pro file it clearly has it in
Code:
SOURCES += resources/main.cpp resources/mainwidget.cpp
I don't know if I missed some step (?) but I fixed it by manually adding -Iheaders/ to the Makefile:
Code:
INCPATH= [a-bunch-of-stuff-that-was-already-there] -Iheaders/

I'm using g++ (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0
 
@JasKinasis thanks so much for this tutorial! I've been looking everywhere for something minimalist like this!

In ran into some issues on the first compile so I'll leave some tips that may be useful for other users:

1 - My compiler was throwing errors like
Code:
error: invalid use of incomplete type ‘class QPushButton’
and the same for QTextBrowser and QGridLayout. I solved it by#includeing them in both mainwidget.h and mainwidget.cpp;

2 - I put my .h files in headers/ and I was having an error on compilation:
Code:
fatal error: mainwidget.h: No such file or directory
even though in the .pro file it clearly has it in
Code:
SOURCES += resources/main.cpp resources/mainwidget.cpp
I don't know if I missed some step (?) but I fixed it by manually adding -Iheaders/ to the Makefile:
Code:
INCPATH= [a-bunch-of-stuff-that-was-already-there] -Iheaders/

I'm using g++ (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0

Interesting..... All of those errors are probably related to the fact that you‘d moved the header files into a separate directory.

Forward declarations have been in C++ for some time, so it should work. The error about incomplete types usually means that the compiler doesn’t know where the cpp/implementation file is.

Including headers for dependencies in the header and the implementation shouldn’t be necessary. You should be able to forward declare dependencies in the header and then include the dependencies in the implementation.

Also, unless a project is extremely large - I prefer to keep things simple and keep all headers and cpp files in a single directory.
So for this example, it seemed overkill to separate the files in different directories.

I’ll take a look another look at the tutorial at some point and will make sure the code still works as it is - in a single directory. JIC anything has changed regarding gcc and forward declarations.

Then I’ll try putting the header and implementation files into different directories and see what additional steps need to be taken to set it up properly for that use case.

If any changes are required, I’ll update the tutorial!

Thank you for your feedback!
 
I Loved this tutorial, so well explained. Thank you so much.
I was trying to implement it for my own project where a button should basically run "blockMesh" (a command for OpenFoam) the Problem is the GUI will be running in a different directory so i came up with this:
process_.start("cd ~/Documents/cavity && blockMesh");

However this doesnt seem to be working.
I tried simpler commands like "ls" just to chek and that didnt work either:

process_.start("cd ~/Documents/cavity && ls");

The text browser just stays blank in both cases. Do you have any idea where the Problem could be? Is there any problem with the fact that it changes directory?

Thanks in advance
 
I Loved this tutorial, so well explained. Thank you so much.
I was trying to implement it for my own project where a button should basically run "blockMesh" (a command for OpenFoam) the Problem is the GUI will be running in a different directory so i came up with this:
process_.start("cd ~/Documents/cavity && blockMesh");

However this doesnt seem to be working.
I tried simpler commands like "ls" just to chek and that didnt work either:

process_.start("cd ~/Documents/cavity && ls");

The text browser just stays blank in both cases. Do you have any idea where the Problem could be? Is there any problem with the fact that it changes directory?

Thanks in advance

Sorry for the late reply - I've been on holiday for a while.
I'll take a more detailed look after work this evening.
From a quick look - it may be because you have used the tilde ~ shortcut.
e.g.
C++:
process_start("cd ~/Documents/cavity && blockMesh");

In my example, I used $HOME/ instead of ~. I can't remember why offhand, but it was probably deliberate. I suspect it's because QT doesn't translate the ~. So it's better to use environment variables like $HOME.

So I think what's happening is - the cd command is failing because of the ~. And then blockMesh is not running because you used the && AND operator, which will only run the command to it's right, if the command to it's left succeeds. So cd fails and then blockMesh does not run. I think that's what we're seeing here.

So the first thing to try would be to change your command to:
C++:
process_start("cd $HOME/Documents/cavity && blockMesh");

That should allow the cd command to succeed and then blockMesh should run and its output should appear in the text-box.

If blockMesh still fails to run - the next thing to do would be to include the path to the blockmesh executable in the command:
e.g.
C++:
process_start("cd $HOME/Documents/cavity && /path/to/blockMesh");

If my first suggestion does not fix this problem, the second one definitely should. At least, from what I can see anyway. But I'll try to verify that later after I've finished work!
 
Last edited:
A bit off-topic:

I've been on holiday for a while.

Good. I was wondering where you'd been, as I'd not seen you post recently. We need someone to answer the programming and scripting questions! Glad you're back! ;)
 
A bit off-topic:



Good. I was wondering where you'd been, as I'd not seen you post recently. We need someone to answer the programming and scripting questions! Glad you're back! ;)
Yes, and I need to post more programming obfuscation techniques and code and have @JasKinasis unravel them in how they work! ;):D

Maybe a buffer overflows followed by the nop slide! All hail 0x90 forever!
 
Sometimes, I find questions up his alley and ping 'em to them. Usually scripting questions, but sometimes programming questions.
 


Top