Introduction

For many, getting started with desktop programming can seem daunting; especially if you intend to make a cross-platform application. Many flock to platforms like Electron for these types of tasks while others look to Java. Here I’m going to show you how to do it with C++, Qt and CMake. These are industry standards that can give you a high performance application that works on Windows, Linux and Mac.

As a more interactive introduction, check out the video below if you want to see each step I discuss below.

Getting Setup

To follow along with this guide you’ll need three things:

  • A C++ IDE/Compiler (I’ll be using Visual Studio 2017 Community Edition)
  • CMake (You can download the latest release here).
  • Qt 5.9 (See instructions below)
    • Note: Since the time this article was written, Qt 5.10 has been released. Feel free to use the latest available version of Qt5.

Setting Up Qt

Downloading the open source version of Qt 5 has become a bit more convoluted recently, but long story short, you can download it here. Just be sure to download the open source edition, not the “free trial”.

Once you have that downloaded, run the installer and you should see something like this:

MaintenanceTool_2017-08-24_20-27-08

The installer for Qt5 has access to the full repository of all the Qt5 releases which you can see listed above. For this example code I’m going to be using the latest official release which is 5.9.1. If you expand the Qt 5.9.1 drop down menu, you’ll see something like what is pictured below:

MaintenanceTool_2017-08-24_20-29-12

Here you’ll want to make sure to install the correct version of the pre-built Qt5 binaries for your IDE/compiler on your system. Since I’m working in Visual Studio (note I use 2015 and 2017) I have both msvc2015 and msvc2017 selected.

Once you’ve selected the correct one for your system proceed through the rest of the steps in the installer (keep all the defaults selected) and you should be good to go!

Setting Up the Project

Creating the Main Project Files

Setting up a basic project is relatively easy with CMake. First though, we’ll need to create our source files for the main window of the GUI and also an entry point for the application. First we’ll start with the source files for the main window of the application.

The easiest way to do this is to use the included QtCreator tool that is bundled in your Qt5 installation package. Simply search for QtCreator on your system and it should pop up.

Once you open it, go to File -> New Project or File. Select “Qt” under Files and Classes and then select Qt Designer Form Class on the right (see image below). 

In the next screen select Main Window and click next. Be sure to save the files in your project’s source directory.

qtcreator_2017-08-24_20-42-41

Once your click Finish in the wizard, Qt Creator will open the files you just created (mainwindow.ui, mainwindow.h, and mainwindow.cpp).

I should note here that you can actually do Qt5 development 100% in QtCreator. QtCreator uses qmake, which is Qt’s own build system. Instead of editing a CMakeLists file, you edit a .pro file (Qt’s project file) and manage all your dependencies and build steps that way. Personally, I prefer an IDE with a bit more power (like Visual Studio) but QtCreator has a powerful, visual GUI editor in it which is a great tool for GUI design.

Great! You’ve made it this far and now we have our source code. If you don’t want to go through the process outlined above, you can grab the code below or just grab it from Github.

Source Code

mainwindow.ui

<ui version="4.0">
 <author/>
 <comment/>
 <exportmacro/>
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>800</width>
    <height>600</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QMenuBar" name="menubar"/>
  <widget class="QWidget" name="centralwidget"/>
  <widget class="QStatusBar" name="statusbar"/>
 </widget>
 <pixmapfunction/>
 <connections/>
</ui>

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include 

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
Q_OBJECT

public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();

private:
Ui::MainWindow *ui;
};

#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
	QMainWindow(parent),
ui(new Ui::MainWindow)
{
	ui->setupUi(this);
}

MainWindow::~MainWindow()
{
delete ui;
}

I’ve also made a basic main cpp file as the entry point for the application:

main.cpp

#include <QApplication>
#include "mainwindow.h"

int main(int argc, char *argv[]){
	QApplication a(argc, argv);
	MainWindow w;
	w.show();
	return a.exec();
}

CMake Time!

Basic Script Setup

Ok so now we’re going to get into our CMake script. You may be wondering though, why use CMake? Well the basic idea of CMake is to separate your source code from your build. In languages other than C++ this isn’t really an issue (think Java, Android, Python C#) as the build steps are pretty well-defined and behave similarly on different machines.

C++ is slightly different because it has to be compiled directly to assembly and in addition to that, there are many, many different compilers out there (msvc, gcc, clang…) for different platforms. So where CMake comes in is that it handles setting up all the dependencies, the build steps and even build order so that you can have one code base for your source but still be able to build on multiple platforms easily. This is especially useful for Qt5 applications since Qt5 is inherently cross-platform.

To get started with our CMake script, open your favorite text editor (I use Sublime 3) and create a file in your project directory called CMakeLists.txt. Note that the name must match exactly, otherwise it won’t be recognized by CMake.

To get our script underway, we’ll add the lines that are required by CMake: A line declaring the project name and a line declaring the minimum required version of CMake. Below are the two lines:

cmake_minimum_required(VERSION 3.0.0 FATAL_ERROR)
project(GettingStartedWithCmake)

Next we’re going to add our dependencies. To do this, we’re going to use the find_package command built into CMake.

# find Qt5 and required components
find_package(Qt5 COMPONENTS REQUIRED Core Gui Widgets)

What this does is it looks for Qt5 on your system and will specifically look for the QtCore, QtWidgets and QtGui components that we need to create a desktop program. If it can’t find Qt5 automatically, CMake will yell at you and tell you to set the Qt5_DIR. This variable should be an absolute path that points to the directory on  your system that contains the Qt5Config.cmake file (usually <qt5_install_dir>/lib/cmake/Qt5).

Now we can add our executable! To do this we’ll use the add_executable macro built into CMake. You can look at the documentation for this function here. Basically you pass in the name of the executable and the source files and now you have what’s called a build target. To do this add the following line:

# add the executable
add_executable(GettingStartedWithCmake ${project_sources} ${project_headers})

Accounting for Qt5 Moc

Normally, you would be done at this point and have a working executable. But, there are two things we still need to do to get everything working:

  1. We need to link our executable to the appropriate Qt5 libraries.
  2. We need to add the moc sources to our executable.

Item 1 seems reasonable; we need to have a way to link our executable to the Qt5 libraries so we can use the Qt classes and components in our code. That makes sense. If you’ve never worked with Qt before though, you may be wondering what the second item even means.

Long story short, Qt requires an extra build step where secondary source files (moc files) are auto generated and contain definitions of signals and other intrinsic QObject functions that you don’t need to worry about. So for our code there will be a moc_mainwindow.cpp file auto generated for us. Additionally, this auto generation allows you to turn the ui file we created into a C++ header that can be included in the project directly. After accounting for these steps, your CMakeLists should look something like this:

cmake_minimum_required(VERSION 3.0.0 FATAL_ERROR)
project(GettingStartedWithCmake LANGUAGES CXX)
# find Qt5 and required components
find_package(Qt5 COMPONENTS REQUIRED Core Gui Widgets)
# need this to include the auto generated ui_mainwindow.h
# file from the qt5_wrap_ui call below.
set(CMAKE_INCLUDE_CURRENT_DIR ON)

set(project_sources mainwindow.cpp main.cpp)
set(project_headers mainwindow.h)
set(project_ui mainwindow.ui)
# wrap the ui file to a c++ header
qt5_wrap_ui(ui_wrap ${project_ui})
qt5_wrap_cpp(moc_sources ${project_headers})
add_executable(GettingStartedWithCmake 
	${project_sources} 
	${project_headers}
	${ui_wrap} 
	${moc_sources})
target_link_libraries(GettingStartedWithCmake 
	PUBLIC
	Qt5::Core
	Qt5::Gui 
	Qt5::Widgets)

# auto copy dlls if on windows
if(WIN32)
	add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
		COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:Qt5::Core> $<TARGET_FILE_DIR:${PROJECT_NAME}>
		COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:Qt5::Gui> $<TARGET_FILE_DIR:${PROJECT_NAME}>
		COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:Qt5::Widgets> $<TARGET_FILE_DIR:${PROJECT_NAME}> )
endif(WIN32)

Once you have this code written head into CMake. Set your source directory to the directory you’ve been writing your code in and set the build directory to be somewhere that makes sense. Click “Configure” in CMake and you’ll get an error asking you to set the Qt5_DIR variable. On my machine I set it to the following path: D:/Qt/5.9.1/msvc2017_64/lib/cmake/Qt5.

Click configure again and CMake should automatically find paths to the Qt5Core, Qt5Gui and Qt5Widgets directories. Click generate and then open your project and build.

If all goes well you should have a Qt5 window showing like the one below! Good work!

Conclusion

Whew, that was a lot of info you got though. But you now have a basic project setup for desktop development and the best thing is you can reuse all this code in your next desktop app as a starting point!

All this only scratches the surface though of what CMake can do and what you can do wtih Qt5. Look for more tutorials in the future on these topics. If you have any questions feel free to contact me or drop a comment down below. Thanks for reading and happy coding!

Leave a Comment

Your email address will not be published. Required fields are marked *

Loading...