Implementing a General Layout Scheme of Top/Left/Main in QT C++

I came across an article discussing this layout implementation, but the code was missing many components and could not run. It seems to be an AI-generated article, primarily emphasizing hands-on skills. Therefore, I borrowed the coding ideas and some code from that article to create a small demo that is functional and has all the source code open-sourced. Feel free to download and run it if you’re interested.Code repository:https://gitee.com/codeceo_net/qttest/tree/TopLeftMain/First, let’s take a look at the effect:Implementing a General Layout Scheme of Top/Left/Main in QT C++The layout consists of a top menu, a left menu, and a right area that loads different interfaces based on the selected menu item. Now, I will briefly list some of the code; the rest can be understood by downloading the source code and running it.imodule.h

#ifndef IMODULE_H
#define IMODULE_H
#include <QString>
#include <QIcon>
#include <QAction>
#include <QWidget>
/// Module class
class IModule {
public:
    IModule();
    // This function must be implemented by subclasses; the base class cannot be instantiated directly.
    virtual ~IModule() = default;
    virtual QString moduleName() const = 0; // Module name
    virtual QIcon moduleIcon() const = 0; // Module icon
    virtual QAction* moduleAction() const = 0; // Corresponding action for the module
    virtual QWidget* moduleWidget() const = 0; // Corresponding main window for the module
};
#endif // IMODULE_H

This class represents the entity of the left menu. When we create a new menu, we assign this menu its layout and some related properties.

LeftMenu.h

#ifndef LEFTMENU_H
#define LEFTMENU_H
#include <QListWidget>
#include "imodule.h"
/// Left menu class
class LeftMenu: public QListWidget {
    Q_OBJECT
public:
    explicit LeftMenu(QWidget* parent = nullptr);
    void addModuleItem(IModule* module); // Add module
    void setCurrentModule(IModule* module); // Set current module
signals:
    void moduleChanged(IModule* module); // Module switch signal
private:
    QMap<QListWidgetItem*, IModule*> m_itemModuleMap; // Mapping of menu items to modules
};
#endif // LEFTMENU_H

leftmenu.cpp

#include "leftmenu.h"
/// Left navigation
LeftMenu::LeftMenu(QWidget* parent): QListWidget(parent) {
    setFixedWidth(200);  // Default width of the left menu
    setMinimumWidth(200);
    setMaximumWidth(200);
    setStyleSheet(R"(
        QListWidget { border: none; background: #f8f9fa; }
        QListWidget::item { height: 40px; padding-left: 15px; background: #ffffff; color: #000000; }
        QListWidget::item:selected { background: #e9ecef; color: #2c3e50; }
    )");
    // Signal-slot connection
    connect(this, &QListWidget::itemClicked, this, [this](QListWidgetItem* item) {
        emit moduleChanged(m_itemModuleMap.value(item));
    });
}
// Add module
void LeftMenu::addModuleItem(IModule* module) {
    if (!module) return;
    QListWidgetItem* item = new QListWidgetItem(module->moduleIcon(), module->moduleName());
    m_itemModuleMap[item] = module;
    addItem(item);
}
// Select current module
void LeftMenu::setCurrentModule(IModule* module) {
    for (auto it = m_itemModuleMap.constBegin(); it != m_itemModuleMap.constEnd(); ++it) {
        if (it.value() == module) {
            setCurrentItem(it.key());
            break;
        }
    }
}

This is the layout class for the left menu, mainly implementing the addition of menus, highlighting the currently selected menu item, and listening to signal-slot connections.

moduleManager.h

#ifndef MODULEMANAGER_H
#define MODULEMANAGER_H
#include <QObject>
#include <mainwindow.h>
#include <imodule.h>
////// rief The ModuleManager class
/// Module management class
class ModuleManager: public QObject {
    Q_OBJECT
public:
    explicit ModuleManager(MainWindow* mainWindow, QObject* parent = nullptr);
    void registerModule(IModule* module); // Register module
    IModule* getModule(const QString& moduleName) const { return m_modules.value(moduleName, nullptr); } // Get module
    // Get list
    QList<IModule*> getAllModules() const { return m_modules.values(); }
    // Check existence
    bool hasModule(const QString& name) const {
        return m_modules.contains(name);
    }
private:
    MainWindow* m_mainWindow = nullptr; // Main window
    QMap<QString, IModule*> m_modules; // Mapping from module name to module
};
#endif // MODULEMANAGER_H

moduleManager.cpp

#include "modulemanager.h"
ModuleManager::ModuleManager(MainWindow* mainWindow, QObject* parent)
    : QObject(parent), m_mainWindow(mainWindow) {}
void ModuleManager::registerModule(IModule* module) {
    if (!module || m_modules.contains(module->moduleName())) return;
    m_modules.insert(module->moduleName(), module);
    // Add menu item to the left menu. How to get it?
    m_mainWindow->getLeftMenu()->addModuleItem(module);
}

This class mainly acts as middleware to manage tools for adding menus to the menu.

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <imodule.h>
#include <QStackedWidget>
#include <QSplitter>
#include <QHBoxLayout>
#include <top.h>
#include <leftmenu.h>
// #include <modulemanager.h>
// QT_BEGIN_NAMESPACE
// namespace Ui {
// class MainWindow;
// }
// QT_END_NAMESPACE
// Declaration to avoid multiple inclusions
class Top;
class LeftMenu;
class ModuleManager;
/// Main interface class
class MainWindow : public QMainWindow {
    Q_OBJECT
public:
    // explicit is a keyword in C++ that prevents the constructor or type conversion operator from being implicitly called, avoiding unexpected implicit type conversions.
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
    // Module management interface
    void registerModule(IModule* module); // Register module
    void switchModule(const QString& moduleName); // Switch module
    void initCentralLayout();
    void initStatusBar();
    void registerDemoModules();
    LeftMenu* getLeftMenu() const { return m_leftMenu; } // Get left menu
protected:
    void resizeEvent(QResizeEvent* event) override; // Window resize event
private:
    // Core components
    Top* m_topBar = nullptr;         // Top navigation bar
    LeftMenu* m_leftMenu = nullptr;     // Left navigation menu
    QStackedWidget* m_contentStack = nullptr; // Main content area (stacked window)
    ModuleManager* m_moduleManager = nullptr;   // Module manager
    // Layout related
    QWidget* m_centralContainer = nullptr;  // Central container (wraps left menu + main content area)
    QHBoxLayout* m_centralLayout = nullptr; // Central container layout
    QSplitter* m_mainSplitter = nullptr;    // Main splitter (left menu and main content area)
    // private:
    //     Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H

mainwindow.cpp

// #include "ui_mainwindow.h"
#include <mainwindow.h>
#include <top.h>
#include <leftmenu.h>
#include <top.h>
#include <modulemanager.h>
#include <QStatusBar>
MainWindow::MainWindow(QWidget* parent)
    : QMainWindow(parent),
    m_topBar(new Top()),
    m_leftMenu(new LeftMenu()),
    m_contentStack(new QStackedWidget()),
    m_moduleManager(new ModuleManager(this)) {
    // Basic window settings
    setWindowTitle("General Interface Framework");
    resize(1200, 800);
    // Initialize core layout
    initCentralLayout();
    initStatusBar();
    // Register demo modules (dynamically loaded in actual development)
    registerDemoModules();
    // Connect signals in m_leftMenu
    // Signal-slot connection
    connect(m_leftMenu, &LeftMenu::moduleChanged, this, [this](IModule* module) {
        qDebug() << "Module clicked:" << module->moduleName();
        //m_contentStack->setCurrentWidget(module->moduleWidget());
        switchModule(module->moduleName());
    });
}
MainWindow::~MainWindow() {}
void MainWindow::registerModule(IModule* module) {
    m_moduleManager->registerModule(module);
}
void MainWindow::switchModule(const QString& moduleName) {
    if (m_moduleManager->hasModule(moduleName)) {  // Should actually get the module from m_moduleManager
        m_leftMenu->setCurrentModule(m_moduleManager->getModule(moduleName));  // Need to supplement the module retrieval interface
        auto widget = m_moduleManager->getModule(moduleName)->moduleWidget();
        if (!widget) {
            qDebug() << "moduleWidget() is null!";
        } else {
            qDebug() << "moduleWidget() exists:" << widget;
        }
        // If not yet added to stack, add it first
        if (m_contentStack->indexOf(widget) == -1) {
            m_contentStack->addWidget(widget);
        }
        m_contentStack->setCurrentWidget(widget);
    }
}
void MainWindow::resizeEvent(QResizeEvent* event) {
    QMainWindow::resizeEvent(event);
    qDebug() << "resizeEvent";
}
void MainWindow::initCentralLayout() {
    // Central container (wraps left menu + main content area)
    m_centralContainer = new QWidget();
    setCentralWidget(m_centralContainer);
    // Main layout (horizontal arrangement: left menu + main content area)
    m_centralLayout = new QHBoxLayout(m_centralContainer);
    m_centralLayout->setContentsMargins(0, 0, 0, 0);
    m_centralLayout->setSpacing(0);
    // Insert top layout
    m_centralLayout->setMenuBar(m_topBar);
    // Main splitter (supports drag to adjust left menu width)
    m_mainSplitter = new QSplitter(Qt::Horizontal);
    m_mainSplitter->addWidget(m_leftMenu);
    m_mainSplitter->addWidget(m_contentStack);
    // Prevent left side from collapsing completely
    //m_mainSplitter->setCollapsible(0, false); // The 0th widget (left menu) cannot collapse
    // Set initial size
    //m_mainSplitter->setSizes({100});
    // Set stretch policy
    //m_mainSplitter->setStretchFactor(0, 0); // Left side (not stretched)
    //m_mainSplitter->setStretchFactor(1, 1); // Right side (mainly stretched)
    m_centralLayout->addWidget(m_mainSplitter);
}
void MainWindow::initStatusBar() {
    QStatusBar* statusBar = this->statusBar();
    statusBar->setStyleSheet("QStatusBar{padding: 4px; background: #ffffff;}");
    statusBar->showMessage("Ready");
}
// Register modules
void MainWindow::registerDemoModules() {
    // Demo module 1: Home
    class HomeModule : public IModule {
        private:
        QAction* m_action;
        public:
        HomeModule() {
            m_action = new QAction(moduleIcon(), moduleName());
            m_action->setShortcut(QKeySequence("Ctrl+1"));
        }
        QString moduleName() const override { return "Home"; }
        QIcon moduleIcon() const override { return QIcon(":/icons/home.png"); }
        QAction* moduleAction() const override {
            return m_action;
        }
        QWidget* moduleWidget() const override {
            QWidget* widget = new QWidget();
            QVBoxLayout* layout = new QVBoxLayout(widget);
            QLabel* qLabel = new QLabel("<h2>Welcome to the system</h2><p>This is the home content area</p>");
            // Color
            qLabel->setStyleSheet("QLabel { color: #34495e; }");
            // Font size
            QFont font = qLabel->font();
            font.setPointSize(14);
            qLabel->setFont(font);
            layout->addWidget(qLabel);
            return widget;
        }
    };
    registerModule(new HomeModule());
    // Demo module 2: Data Statistics
    class StatsModule : public IModule {
        private:
        QAction* m_action;
        public:
        StatsModule() {
            m_action = new QAction(moduleIcon(), moduleName());
            m_action->setShortcut(QKeySequence("Ctrl+2"));
        }
        QString moduleName() const override { return "Data Statistics"; }
        QIcon moduleIcon() const override {
            return QIcon(":/icons/stat.png");
        }
        QAction* moduleAction() const override {
            return m_action;
        }
        QWidget* moduleWidget() const override {
            QWidget* widget = new QWidget();
            QVBoxLayout* layout = new QVBoxLayout(widget);
            QLabel* qLabel = new QLabel("<h2>Welcome to the system</h2><p>System data statistics</p>");
            // Color
            qLabel->setStyleSheet("QLabel { color: #34495e; }");
            // Font size
            QFont font = qLabel->font();
            font.setPointSize(14);
            qLabel->setFont(font);
            layout->addWidget(qLabel);
            return widget;
        }
    };
    registerModule(new StatsModule());
    // Default display home
    switchModule("Home");
}

Next is the main interface, registering menus, adding layouts, connecting information slots, and other functionalities.

Finally, the entry function main.cpp

#include "mainwindow.h"
#include <QApplication>
#include <QStyleFactory>
int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    // Global style settings (optional)
    app.setStyle(QStyleFactory::create("Fusion"));
    QPalette palette;
    palette.setColor(QPalette::Window, QColor(240, 240, 240));
    app.setPalette(palette);
    MainWindow mainWindow;
    mainWindow.show();
    return app.exec();
}

Okay, enough talk, just download the source code and experiment!

Follow me for more basic programming knowledge

Implementing a General Layout Scheme of Top/Left/Main in QT C++

👆🏻👆🏻👆🏻Scan to follow👆🏻👆🏻👆🏻

Click “Implementing a General Layout Scheme of Top/Left/Main in QT C++” to like, it motivates me to keep updating

Blog address: blog.codeceo.net

Reference article: General Interface Layout Framework Scheme in Qt C++

Leave a Comment