building a simple qt 6 application — basic plumbing

I’m continuing to work on my simple Qt 6 application. I’ve begun to break up the source files into smaller compilation modules (files) that perform a specific task or small set of related tasks. This has allowed me to add the framework for the application to save its operating state and then recover it when it’s restarted. I want that capability because I hate applications that don’t pick up where I left off when I closed the application.

The first change has been to the CMake file, CMakeLists.txt.

cmake_minimum_required (VERSION 3.25.0)
# Build requirements
include_directories( /usr/include/x86_64-linux-gnu/qt6/
                     /usr/include/x86_64-linux-gnu/qt6/QtWidgets/ )
link_libraries( Qt6Core Qt6Widgets Qt6Gui )
add_compile_options( -fPIC -std=c++20 )
# Project specific
project (control-app)
add_executable (control-app

The first change is the addition of two more include directories on lines 6 and 7. The add_executable CMake call now has three source files instead of the original one.

The next big change was to break up the original single source file into three. The original source file was renamed main.cpp, and the source that defined the AppWindow functionality was put into two files, one for the main part of AppWindow and the other for just creating the File menu structure. Let’s go in the file order of CMakeLists.txt.

The first file is main.cpp.

#include "AppWindow.hpp"

int main(int argc, char **argv) {
    QApplication app (argc, argv);

    AppWindow window;;

    return app.exec();

This file is what is left of the original simple-app.cpp, stripped of everything not needed and renamed. The only functionality required of main is to instantiate AppWindow and then kick everything off with

#pragma once

#include <QApplication>
#include <QMainWindow>
#include <QMenuBar>
#include <QPlainTextEdit>
#include <QSettings>
#include <QString>

class AppWindow : public QMainWindow {
    const QString SCOPE_NAME{"Qt6BasedSoftware"};
    const QString APP_NAME{"ControlApp"};
    const QString APPWIN_GEOMETRY{"AppWindow/geometry"};
    const QString APPWIN_STATE{"AppWindow/state"};
    const int INITIAL_WIDTH{800};
    const int INITIAL_HEIGHT{600};


    QSettings *settings;
    QPlainTextEdit *textEdit;

    void createFileMenu();

    QMenu *fileMenu;
    QAction *newAction;
    QAction *openAction;
    QAction *saveAction;
    QAction *saveAsAction;
    QAction *aboutAction;
    QAction *exitAction;

    void closeEvent(QCloseEvent*);

If you compare this header file with the AppWindow class definition in simple-app.cpp, you’ll notice that the header file alone is longer than the original simple-app.cpp. At this point there aren’t all that many class functions; there’s the public constructor and then the private function createFileMenu which can only be called internally to the class, specifically from the constructor. The majority of the header file so far is primarily data structures, although I expect that will change as this application continues to evolve.

#include <QScreen>
#include <QSize>
#include <QStatusBar>
#include <iostream>

#include "AppWindow.hpp"

AppWindow::AppWindow() {
    settings = new QSettings(SCOPE_NAME, APP_NAME);

    textEdit = new QPlainTextEdit;
    statusBar()->showMessage(tr("Statusbar Placeholder"));

    if (settings->contains(APPWIN_GEOMETRY) and 
        settings->contains(APPWIN_STATE)) {
        std::cout << APP_NAME.toStdString() << ": found full state\n";
    else {
        std::cout << APP_NAME.toStdString() << ": did not find full state\n";
        QScreen *primaryScreen = QGuiApplication::primaryScreen();
        QSize screenSize = primaryScreen->availableSize();
        auto screenWidth  = screenSize.width();
        auto screenHeight = screenSize.height();
        auto upperLeftX  = (screenWidth - INITIAL_WIDTH)/2;
        auto upperLeftY = (screenHeight - INITIAL_HEIGHT)/2;
        setGeometry(upperLeftX, upperLeftY, INITIAL_WIDTH, INITIAL_HEIGHT);

void AppWindow::closeEvent(QCloseEvent *event) {
    std::cout << APP_NAME.toStdString() << ": close event\n";
    settings->setValue(APPWIN_GEOMETRY, saveGeometry());
    settings->setValue(APPWIN_STATE, saveState());

This is the meat of the application so far. There are two key sections, the constructor and the definition of function closeEvent.

The constructor sets up the overall environment for QSettings so that the application can save, and then recall, its state when it starts up and shuts down respectively. I followed the directions on the QSettings documentation page ( ). Inside the constructor, starting on line 19, I check to see if the application geometry and state are available from a prior run. If they are not, then I default to a width and a height and a location that places the application squarely in the middle of the screen. If the geometry and state are loaded, however, the application goes back to the state that was last saved. Note that, on 13, that a QPlainTextEdit widget is set as the center widget for AppWindow. This is because Qt states that simply showing an empty QMainWindow isn’t supported (well, they won’t guarantee it works), so I added this widget just to fill the slot. In fact lines 13 through 16 are basically enabling the application (center widget, menu, window title and status line).

In order to make sure that the geometry and state or saved, I’ve redefined QMainWindow::closeEvent() such that everything is saved. After saving the call to QMainWindow::closeEvent() is chained to make sure that the Qt application closes properly.

#include "AppWindow.hpp"

void AppWindow::createFileMenu() {
    fileMenu = menuBar()->addMenu(tr("&File"));
    newAction = new QAction(tr("&New"), this);
    openAction = new QAction(tr("&Open"), this);
    saveAction = new QAction(tr("&Save"), this);
    saveAsAction = new QAction(tr("Save as"), this);
    aboutAction = new QAction(tr("&About Qt"), this);
    connect(aboutAction, &QAction::triggered, qApp, &QApplication::aboutQt);
    exitAction = new QAction(tr("E&xit"), this);
    connect(exitAction, &QAction::triggered, this, &QWidget::close);

The class function that builds the File menu is in its own compilation unit. I did this to keep any one source file from growing too large. I believe in fine granularity. As more menus are needed, and defined, they’ll go into their own files. Additional functionality will also go into their own unique files.

More to come…


building a simple qt 6 application with cmake and make


One thought on “building a simple qt 6 application — basic plumbing

Comments are closed.