QT Project Folder Structure, writing clean code
.txt
QT Project Folder Structure, writing clean code

.txt
cmake_minimum_required(VERSION 3.16)
project(NotepadPro LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON)
Enable Qt MOC/UIC/RCC
set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) set(CMAKE_AUTOUIC ON)
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core) find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Widgets ) qt_standard_project_setup()
qt_add_executable(NotepadPro main.cpp
src/ui/MainWindow.h src/ui/MainWindow.cpp src/ui/MainWindow.ui src/ui/EditorWidget.h src/ui/EditorWidget.cpp
src/core/Document.h src/core/Document.cpp src/core/FileManager.h src/core/FileManager.cpp src/core/SettingsManager.h src/core/SettingsManager.cpp )
target_include_directories(NotepadPro PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src )
set_target_properties(${PROJECT_NAME} PROPERTIES WIN32_EXECUTABLE TRUE )
target_link_libraries(${PROJECT_NAME} PUBLIC Qt::Core Qt::Gui Qt::Widgets )
— — -
pragma once
include <QString>
class Document { public: Document() = default;
void setFilePath(const QString& path); QString filePath() const;
QString displayName() const;
bool isModified() const; void setModified(bool modified);
bool isUntitled() const;
private: QString m_filePath; bool m_modified = false; bool m_isUntitled = true; };
— — — — — — — — — — -
include “Document.h”
include <QFileInfo>
void Document::setFilePath(const QString& path) { m_filePath = path; m_isUntitled = false; }
QString Document::filePath() const { return m_filePath; }
QString Document::displayName() const { if (m_isUntitled) return “Untitled”;
return QFileInfo(m_filePath).fileName(); }
bool Document::isModified() const { return m_modified; }
void Document::setModified(bool modified) { m_modified = modified; }
bool Document::isUntitled() const { return m_isUntitled; }
— —
include “FileManager.h”
include <QFile>
include <QTextStream>
std::optional<QString> FileManager::readFile(const QString& path) { QFile file(path);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) return std::nullopt;
QTextStream in(&file); in.setEncoding(QStringConverter::Utf8);
return in.readAll(); }
bool FileManager::writeFile(const QString& path, const QString& content) { QFile file(path);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) return false;
QTextStream out(&file); out.setEncoding(QStringConverter::Utf8); out << content;
return true; }
— — -—
pragma once
include <QString>
include <optional>
class FileManager { public: static std::optional<QString> readFile(const QString& path); static bool writeFile(const QString& path, const QString& content); };
— — — — — — — —
include “EditorWidget.h”
include “../core/Document.h”
include <QPlainTextEdit>
include <QVBoxLayout>
include <QTextDocument>
EditorWidget::EditorWidget(QWidget* parent) : QWidget(parent), m_document(std::make_unique<Document>()) { m_editor = new QPlainTextEdit(this);
auto* layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(m_editor);
connect(m_editor->document(), &QTextDocument::modificationChanged, this, [this](bool modified) { m_document->setModified(modified); emit modificationChanged(modified); }); }
Document* EditorWidget::document() const { return m_document.get(); }
QString EditorWidget::text() const { return m_editor->toPlainText(); }
void EditorWidget::setText(const QString& text) { m_editor->setPlainText(text); m_document->setModified(false); }
— —
pragma once
include <QWidget>
include <memory>
class QPlainTextEdit; class Document;
class EditorWidget : public QWidget { Q_OBJECT
public: explicit EditorWidget(QWidget* parent = nullptr); ~EditorWidget() override = default;
Document* document() const; QString text() const; void setText(const QString& text);
signals: void modificationChanged(bool modified);
private: QPlainTextEdit* m_editor = nullptr; std::unique_ptr<Document> m_document; };
— — -
include <QTabWidget>
include <QStatusBar>
include <QLabel>
include <QMenuBar>
include <QFileDialog>
include <QMessageBox>
include <QFileInfo>
include <QAction>
include <QCloseEvent>
include “MainWindow.h”
include “../core/FileManager.h”
include “../core/Document.h”
MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) { setupUi(); }
void MainWindow::setupUi() { // Tab Widget (central) m_tabWidget = new QTabWidget(this); m_tabWidget->setTabsClosable(true); m_tabWidget->setMovable(true); m_tabWidget->setDocumentMode(true);
setCentralWidget(m_tabWidget);
// Status Bar m_statusLabel = new QLabel(“Ready”, this); statusBar()->addPermanentWidget(m_statusLabel);
resize(1000, 700); connect(m_tabWidget, &QTabWidget::tabCloseRequested, this, [this](int index) { auto editor = qobject_cast<EditorWidget>(m_tabWidget->widget(index)); if (!editor) return;
if (!maybeSave(editor)) return;
m_tabWidget->removeTab(index); editor->deleteLater(); }); setupMenus(); }
void MainWindow::setupMenus() { m_fileMenu = menuBar()->addMenu(“&File”);
m_actionNew = m_fileMenu->addAction(“&New”); m_actionOpen = m_fileMenu->addAction(“&Open…”); m_actionSave = m_fileMenu->addAction(“&Save”); m_actionSaveAs = m_fileMenu->addAction(“Save &As…”); m_fileMenu->addSeparator(); m_actionExit = m_fileMenu->addAction(“E&xit”);
connect(m_actionNew, &QAction::triggered, this, &MainWindow::createNewTab); connect(m_actionOpen, &QAction::triggered, this, &MainWindow::openFile); connect(m_actionSave, &QAction::triggered, this, &MainWindow::saveFile); connect(m_actionSaveAs, &QAction::triggered, this, &MainWindow::saveFileAs); connect(m_actionExit, &QAction::triggered, this, &QWidget::close); }
void MainWindow::createNewTab() { auto* editor = new EditorWidget(this);
QString title = QString(“Untitled %1”).arg(m_untitledCounter++); addEditorTab(editor, title); }
EditorWidget MainWindow::currentEditor() const { return qobject_cast<EditorWidget>(m_tabWidget->currentWidget()); }
EditorWidget* MainWindow::findOpenEditor(const QString& path) { QString normalized = QFileInfo(path).canonicalFilePath();
for (int i = 0; i < m_tabWidget->count(); ++i) { auto editor = qobject_cast<EditorWidget>(m_tabWidget->widget(i)); if (!editor) continue;
QString openPath = QFileInfo(editor->document()->filePath()).canonicalFilePath();
if (!openPath.isEmpty() && openPath == normalized) return editor; }
return nullptr; }
void MainWindow::openFile() { QString path = QFileDialog::getOpenFileName(this, “Open File”);
if (path.isEmpty()) return;
if (auto* existing = findOpenEditor(path)) { m_tabWidget->setCurrentWidget(existing); return; }
auto content = FileManager::readFile(path); if (!content) { QMessageBox::warning(this, “Error”, “Could not open file.”); return; }
auto editor = new EditorWidget(this); editor->setText(content); editor->document()->setFilePath(path);
addEditorTab(editor, QFileInfo(path).fileName()); }
void MainWindow::saveFile() { auto* editor = currentEditor(); if (!editor) return;
if (editor->document()->isUntitled()) { saveFileAs(); return; }
QString path = editor->document()->filePath();
if (!FileManager::writeFile(path, editor->text())) { QMessageBox::warning(this, “Error”, “Could not save file.”); return; }
editor->document()->setModified(false); }
void MainWindow::saveFileAs() { auto* editor = currentEditor(); if (!editor) return;
QString path = QFileDialog::getSaveFileName(this, “Save File As”);
if (path.isEmpty()) return;
if (!FileManager::writeFile(path, editor->text())) { QMessageBox::warning(this, “Error”, “Could not save file.”); return; }
editor->document()->setFilePath(path); editor->document()->setModified(false);
int index = m_tabWidget->indexOf(editor); m_tabWidget->setTabText(index, QFileInfo(path).fileName()); }
void MainWindow::addEditorTab(EditorWidget* editor, const QString& title) { m_tabWidget->addTab(editor, title); m_tabWidget->setCurrentWidget(editor);
connect(editor, &EditorWidget::modificationChanged, this, [this, editor](bool modified) { int index = m_tabWidget->indexOf(editor); if (index == -1) return;
QString tabTitle = editor->document()->displayName(); if (modified) tabTitle += “*”;
m_tabWidget->setTabText(index, tabTitle); }); }
bool MainWindow::maybeSave(EditorWidget* editor) { if (!editor->document()->isModified()) return true;
QString name = editor->document()->displayName();
auto ret = QMessageBox::warning( this, “Unsaved Changes”, QString(“‘%1’ has unsaved changes.\nDo you want to save?”).arg(name), QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel );
if (ret == QMessageBox::Save) { m_tabWidget->setCurrentWidget(editor); saveFile(); return !editor->document()->isModified(); } else if (ret == QMessageBox::Cancel) { return false; }
return true; // Discard }
void MainWindow::closeEvent(QCloseEvent event) { for (int i = 0; i < m_tabWidget->count(); ++i) { auto editor = qobject_cast<EditorWidget*>(m_tabWidget->widget(i)); if (!editor) continue;
if (!maybeSave(editor)) { event->ignore(); return; } }
event->accept(); }
— —
pragma once
include <QMainWindow>
include “EditorWidget.h”
class QTabWidget; class QLabel; class QAction; class QMenu;
class MainWindow : public QMainWindow { Q_OBJECT
public: explicit MainWindow(QWidget* parent = nullptr); ~MainWindow() override = default;
private: void setupMenus(); void createNewTab(); void openFile(); void saveFile(); void saveFileAs(); EditorWidget currentEditor() const; EditorWidget findOpenEditor(const QString& path); void addEditorTab(EditorWidget editor, const QString& title); bool maybeSave(EditorWidget editor);
private: QMenu* m_fileMenu = nullptr;
QAction m_actionNew = nullptr; QAction m_actionOpen = nullptr; QAction m_actionSave = nullptr; QAction m_actionSaveAs = nullptr; QAction* m_actionExit = nullptr;
int m_untitledCounter = 1;
private: void setupUi();
private: QTabWidget m_tabWidget = nullptr; QLabel m_statusLabel = nullptr;
protected: void closeEvent(QCloseEvent* event) override; };
— —
<?xml version=”1.0" encoding=”UTF-8"?> <ui version=”4.0"> <class>MainWindow</class> <widget class=”QMainWindow” name=”MainWindow”> <property name=”geometry”> <rect> <x>0</x> <y>0</y> <width>1000</width> <height>700</height> </rect> </property> <property name=”windowTitle”> <string>Notepad Clone</string> </property>
<! — CENTRAL WIDGET → <widget class=”QWidget” name=”centralwidget”> <layout class=”QVBoxLayout” name=”verticalLayout”> <property name=”spacing”> <number>0</number> </property> <property name=”leftMargin”> <number>0</number> </property> <property name=”topMargin”> <number>0</number> </property> <property name=”rightMargin”> <number>0</number> </property> <property name=”bottomMargin”> <number>0</number> </property>
<item> <widget class=”QTabWidget” name=”tabWidget”> <property name=”tabsClosable”> <bool>true</bool> </property> <property name=”movable”> <bool>true</bool> </property> <property name=”documentMode”> <bool>true</bool> </property> <property name=”tabPosition”> <enum>QTabWidget::North</enum> </property> </widget> </item>
</layout> </widget>
<! — MENU BAR → <widget class=”QMenuBar” name=”menubar”>
<widget class=”QMenu” name=”menuFile”> <property name=”title”> <string>&File</string> </property> <addaction name=”actionNew”/> <addaction name=”actionOpen”/> <addaction name=”actionSave”/> <addaction name=”actionSaveAs”/> <addaction name=”separator”/> <addaction name=”actionExit”/> </widget>
<widget class=”QMenu” name=”menuEdit”> <property name=”title”> <string>&Edit</string> </property> </widget>
<widget class=”QMenu” name=”menuFormat”> <property name=”title”> <string>F&ormat</string> </property> </widget>
<widget class=”QMenu” name=”menuView”> <property name=”title”> <string>&View</string> </property> </widget>
<widget class=”QMenu” name=”menuHelp”> <property name=”title”> <string>&Help</string> </property> </widget>
<addaction name=”menuFile”/> <addaction name=”menuEdit”/> <addaction name=”menuFormat”/> <addaction name=”menuView”/> <addaction name=”menuHelp”/>
</widget>
<! — STATUS BAR → <widget class=”QStatusBar” name=”statusbar”/>
<! — ACTIONS →
<action name=”actionNew”> <property name=”text”> <string>&New</string> </property> <property name=”shortcut”> <string>Ctrl+N</string> </property> </action>
<action name=”actionOpen”> <property name=”text”> <string>&Open…</string> </property> <property name=”shortcut”> <string>Ctrl+O</string> </property> </action>
<action name=”actionSave”> <property name=”text”> <string>&Save</string> </property> <property name=”shortcut”> <string>Ctrl+S</string> </property> </action>
<action name=”actionSaveAs”> <property name=”text”> <string>Save &As…</string> </property> <property name=”shortcut”> <string>Ctrl+Shift+S</string> </property> </action>
<action name=”actionExit”> <property name=”text”> <string>E&xit</string> </property> <property name=”shortcut”> <string>Alt+F4</string> </property> </action>
</widget>
<resources/> <connections/> </ui>
— —
include “MainWindow.h”
include “ui_MainWindow.h”
include “EditorWidget.h”
include “../core/FileManager.h”
include <QTabWidget>
include <QFileDialog>
include <QMessageBox>
include <QFileInfo>
include <QLabel>
include <QCloseEvent>
MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this);
// Status bar label m_statusLabel = new QLabel(“Ready”, this); statusBar()->addPermanentWidget(m_statusLabel);
// Configure tab widget ui->tabWidget->setTabsClosable(true); ui->tabWidget->setMovable(true); ui->tabWidget->setDocumentMode(true);
// Close tab handling connect(ui->tabWidget, &QTabWidget::tabCloseRequested, this, [this](int index) { auto editor = qobject_cast<EditorWidget>(ui->tabWidget->widget(index));
if (!editor) return;
if (!maybeSave(editor)) return;
ui->tabWidget->removeTab(index); editor->deleteLater(); }); }
MainWindow::~MainWindow() { delete ui; }
EditorWidget MainWindow::currentEditor() const { return qobject_cast<EditorWidget>(ui->tabWidget->currentWidget()); }
void MainWindow::addEditorTab(EditorWidget* editor, const QString& title) { ui->tabWidget->addTab(editor, title); ui->tabWidget->setCurrentWidget(editor);
connect(editor, &EditorWidget::modificationChanged, this, [this, editor](bool modified) { int index = ui->tabWidget->indexOf(editor);
if (index == -1) return;
QString title = editor->document()->displayName();
if (modified) title += “*”;
ui->tabWidget->setTabText(index, title); }); }
EditorWidget* MainWindow::findOpenEditor(const QString& path) { QString normalized = QFileInfo(path).canonicalFilePath();
for (int i = 0; i < ui->tabWidget->count(); ++i) { auto editor = qobject_cast<EditorWidget>(ui->tabWidget->widget(i));
if (!editor) continue;
QString openPath = QFileInfo(editor->document()->filePath()).canonicalFilePath();
if (!openPath.isEmpty() && openPath == normalized) return editor; }
return nullptr; }
void MainWindow::on_actionNew_triggered() { auto* editor = new EditorWidget(this);
QString title = QString(“Untitled %1”).arg(m_untitledCounter++);
addEditorTab(editor, title); }
void MainWindow::on_actionOpen_triggered() { QString path = QFileDialog::getOpenFileName(this, “Open File”);
if (path.isEmpty()) return;
// Prevent duplicate open if (auto* existing = findOpenEditor(path)) { ui->tabWidget->setCurrentWidget(existing); return; }
auto content = FileManager::readFile(path);
if (!content) { QMessageBox::warning(this, “Error”, “Could not open file.”); return; }
auto* editor = new EditorWidget(this);
editor->setText(*content); editor->document()->setFilePath(path);
addEditorTab(editor, QFileInfo(path).fileName()); }
void MainWindow::on_actionSave_triggered() { auto* editor = currentEditor();
if (!editor) return;
if (editor->document()->isUntitled()) { on_actionSaveAs_triggered(); return; }
QString path = editor->document()->filePath();
if (!FileManager::writeFile(path, editor->text())) { QMessageBox::warning(this, “Error”, “Could not save file.”); return; }
editor->document()->setModified(false); }
void MainWindow::on_actionSaveAs_triggered() { auto* editor = currentEditor();
if (!editor) return;
QString path = QFileDialog::getSaveFileName(this, “Save File As”);
if (path.isEmpty()) return;
if (!FileManager::writeFile(path, editor->text())) { QMessageBox::warning(this, “Error”, “Could not save file.”); return; }
editor->document()->setFilePath(path); editor->document()->setModified(false);
int index = ui->tabWidget->indexOf(editor);
ui->tabWidget->setTabText(index, QFileInfo(path).fileName()); }
void MainWindow::on_actionExit_triggered() { close(); }
bool MainWindow::maybeSave(EditorWidget* editor) { if (!editor->document()->isModified()) return true;
QString name = editor->document()->displayName();
auto ret = QMessageBox::warning( this, “Unsaved Changes”, QString(“‘%1’ has unsaved changes.\nDo you want to save?”) .arg(name), QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel );
if (ret == QMessageBox::Save) { ui->tabWidget->setCurrentWidget(editor); on_actionSave_triggered();
return !editor->document()->isModified(); } else if (ret == QMessageBox::Cancel) { return false; }
return true; }
void MainWindow::closeEvent(QCloseEvent event) { for (int i = 0; i < ui->tabWidget->count(); ++i) { auto editor = qobject_cast<EditorWidget*>(ui->tabWidget->widget(i));
if (!editor) continue;
if (!maybeSave(editor)) { event->ignore(); return; } }
event->accept(); }
메타데이터
- post_id
- 5fc52abb64d2
- slug
- qt-project-folder-structure-writing-clean-code-5fc52abb64d2
- url
- https://medium.com/@danielgakwayya/qt-project-folder-structure-writing-clean-code-5fc52abb64d2
- canonical_url
- https://medium.com/@danielgakwayya/qt-project-folder-structure-writing-clean-code-5fc52abb64d2
- author_url
- https://medium.com/@danielgakwayya
- status
- ok
- fetched_at
- 2026-06-23 03:48:11