C++ GUI Applications
Building desktop applications with graphical user interfaces
🖥️ What are GUI Applications?
GUI applications provide visual interfaces with windows, buttons, and interactive elements using frameworks like Qt, wxWidgets, FLTK, and ImGui for desktop software development.
// Simple Qt application structure
#include <QApplication>
#include <QPushButton>
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
QPushButton button("Hello World!");
button.show();
return app.exec();
}
Output:
[Window opens with "Hello World!" button]
Popular GUI Frameworks
Qt
Cross-platform with rich widgets
QWidget window;
QPushButton button("Click me");
window.show();
wxWidgets
Native look and feel
wxFrame* frame = new wxFrame(
nullptr, wxID_ANY, "Hello");
frame->Show();
FLTK
Lightweight and fast
Fl_Window window(300, 200);
Fl_Button button(100, 50,
100, 30, "Button");
window.show();
ImGui
Immediate mode for tools
ImGui::Begin("Window");
if (ImGui::Button("Click"))
clicked = true;
ImGui::End();
🔹 Simple Calculator (Qt Style)
The Simple Calculator (Qt Style) simulates a Qt-based graphical calculator interface for
performing basic arithmetic. It handles operations like addition,
subtraction, multiplication, and division, displaying formatted results such
as 25 and 40.000000. Built with Qt's signal-slot architecture, it provides responsive
feedback and clear visual output, mimicking real widget behavior. This example demonstrates how to structure GUI
applications using event-driven principles and proper layout management, essential for building interactive desktop
tools in C++ with Qt.
#include <iostream>
#include <string>
#include <functional>
#include <map>
// Simplified GUI Calculator Class
class Calculator {
private:
double currentValue;
double storedValue;
std::string currentOperation;
std::string display;
public:
Calculator() : currentValue(0), storedValue(0), display("0") {}
void inputNumber(int number) {
if (display == "0") {
display = std::to_string(number);
} else {
display += std::to_string(number);
}
currentValue = std::stod(display);
updateDisplay();
}
void inputOperation(const std::string& op) {
if (!currentOperation.empty()) {
calculate();
}
storedValue = currentValue;
currentOperation = op;
display = "0";
std::cout << "Operation: " << op << std::endl;
}
void calculate() {
if (currentOperation.empty()) return;
double result = 0;
if (currentOperation == "+") {
result = storedValue + currentValue;
} else if (currentOperation == "-") {
result = storedValue - currentValue;
} else if (currentOperation == "*") {
result = storedValue * currentValue;
} else if (currentOperation == "/") {
if (currentValue != 0) {
result = storedValue / currentValue;
} else {
std::cout << "Error: Division by zero!" << std::endl;
return;
}
}
currentValue = result;
display = std::to_string(result);
currentOperation.clear();
updateDisplay();
}
void clear() {
currentValue = 0;
storedValue = 0;
currentOperation.clear();
display = "0";
updateDisplay();
}
std::string getDisplay() const {
return display;
}
private:
void updateDisplay() {
std::cout << "Display: " << display << std::endl;
}
};
// Simulate button clicks
int main() {
Calculator calc;
std::cout << "=== Calculator Demo ===" << std::endl;
// Simulate: 15 + 25 = 40
calc.inputNumber(1);
calc.inputNumber(5);
calc.inputOperation("+");
calc.inputNumber(2);
calc.inputNumber(5);
calc.calculate();
std::cout << "Final result: " << calc.getDisplay() << std::endl;
return 0;
}
Output:
=== Calculator Demo ===
Display: 1
Display: 15
Operation: +
Display: 2
Display: 25
Display: 40.000000
Final result: 40.000000
🔹 Event-Driven Programming
Event-Driven Programming is a core paradigm for creating responsive GUI applications where user
actions trigger events. It involves components like windows and buttons that emit events such as
Button Click or Window Close. For example, clicking buttons labeled 'Save',
'Load', or 'Exit' executes corresponding handlers, while closing a window like
My Application triggers cleanup routines. This non-blocking approach uses event loops to listen for and
dispatch actions, ensuring smooth, interactive user experiences in frameworks like Qt, Windows Forms, or web
browsers.
#include <iostream>
#include <vector>
#include <functional>
#include <string>
enum class EventType {
BUTTON_CLICK,
TEXT_CHANGED,
WINDOW_CLOSE
};
struct Event {
EventType type;
std::string data;
Event(EventType t, const std::string& d = "") : type(t), data(d) {}
};
class EventHandler {
public:
using EventCallback = std::function<void(const Event&)>;
private:
std::vector<std::pair<EventType, EventCallback>> handlers;
public:
void subscribe(EventType type, EventCallback callback) {
handlers.emplace_back(type, callback);
}
void emit(const Event& event) {
std::cout << "Event emitted: " << eventTypeToString(event.type) << std::endl;
for (const auto& handler : handlers) {
if (handler.first == event.type) {
handler.second(event);
}
}
}
private:
std::string eventTypeToString(EventType type) {
switch (type) {
case EventType::BUTTON_CLICK: return "Button Click";
case EventType::TEXT_CHANGED: return "Text Changed";
case EventType::WINDOW_CLOSE: return "Window Close";
default: return "Unknown";
}
}
};
class SimpleWindow {
private:
EventHandler eventHandler;
std::string title;
bool isOpen;
public:
SimpleWindow(const std::string& windowTitle) : title(windowTitle), isOpen(true) {
// Subscribe to events
eventHandler.subscribe(EventType::BUTTON_CLICK,
[this](const Event& e) { onButtonClick(e); });
eventHandler.subscribe(EventType::WINDOW_CLOSE,
[this](const Event& e) { onWindowClose(e); });
std::cout << "Window created: " << title << std::endl;
}
void simulateButtonClick(const std::string& buttonName) {
if (isOpen) {
eventHandler.emit(Event(EventType::BUTTON_CLICK, buttonName));
}
}
void simulateClose() {
if (isOpen) {
eventHandler.emit(Event(EventType::WINDOW_CLOSE, title));
}
}
bool getIsOpen() const { return isOpen; }
private:
void onButtonClick(const Event& event) {
std::cout << "Button '" << event.data << "' was clicked!" << std::endl;
if (event.data == "Exit") {
simulateClose();
}
}
void onWindowClose(const Event& event) {
std::cout << "Closing window: " << event.data << std::endl;
isOpen = false;
}
};
int main() {
SimpleWindow window("My Application");
// Simulate user interactions
window.simulateButtonClick("Save");
window.simulateButtonClick("Load");
window.simulateButtonClick("Exit");
std::cout << "Window is " << (window.getIsOpen() ? "open" : "closed") << std::endl;
return 0;
}
Output:
Window created: My Application
Event emitted: Button Click
Button 'Save' was clicked!
Event emitted: Button Click
Button 'Load' was clicked!
Event emitted: Button Click
Button 'Exit' was clicked!
Event emitted: Window Close
Closing window: My Application
Window is closed
🔹 Simple Layout Manager
The Simple Layout Manager automates the positioning and arrangement of GUI elements within
defined geometric bounds. It organizes widgets like buttons (OK, Cancel) and
labels (Status) using layouts such as Horizontal Layout, specifying each element's
rectangle (e.g., Rect(10, 10, 96, 40)). This manager handles dynamic resizing, alignment, and spacing,
ensuring consistent interfaces across different screen sizes. By abstracting manual positioning, it simplifies UI
development, improves visual consistency, and is foundational in toolkits like Qt, GTK, or custom GUI frameworks.
#include <iostream>
#include <vector>
#include <string>
#include <memory>
struct Rectangle {
int x, y, width, height;
Rectangle(int x = 0, int y = 0, int w = 0, int h = 0)
: x(x), y(y), width(w), height(h) {}
void print() const {
std::cout << "Rect(" << x << ", " << y << ", "
<< width << ", " << height << ")" << std::endl;
}
};
class Widget {
protected:
Rectangle bounds;
std::string name;
public:
Widget(const std::string& n) : name(n) {}
virtual ~Widget() = default;
virtual void setBounds(const Rectangle& rect) {
bounds = rect;
}
Rectangle getBounds() const { return bounds; }
std::string getName() const { return name; }
virtual void draw() const {
std::cout << name << " at ";
bounds.print();
}
};
class Button : public Widget {
public:
Button(const std::string& text) : Widget("Button: " + text) {}
};
class Label : public Widget {
public:
Label(const std::string& text) : Widget("Label: " + text) {}
};
class HorizontalLayout {
private:
std::vector<std::unique_ptr<Widget>> widgets;
Rectangle containerBounds;
int spacing;
public:
HorizontalLayout(const Rectangle& bounds, int s = 5)
: containerBounds(bounds), spacing(s) {}
void addWidget(std::unique_ptr<Widget> widget) {
widgets.push_back(std::move(widget));
updateLayout();
}
void updateLayout() {
if (widgets.empty()) return;
int widgetWidth = (containerBounds.width - spacing * (widgets.size() - 1)) / widgets.size();
int currentX = containerBounds.x;
for (auto& widget : widgets) {
Rectangle widgetBounds(currentX, containerBounds.y,
widgetWidth, containerBounds.height);
widget->setBounds(widgetBounds);
currentX += widgetWidth + spacing;
}
}
void draw() const {
std::cout << "=== Horizontal Layout ===" << std::endl;
for (const auto& widget : widgets) {
widget->draw();
}
}
};
int main() {
// Create a horizontal layout
HorizontalLayout layout(Rectangle(10, 10, 300, 40));
// Add widgets
layout.addWidget(std::make_unique<Button>("OK"));
layout.addWidget(std::make_unique<Button>("Cancel"));
layout.addWidget(std::make_unique<Label>("Status"));
// Draw the layout
layout.draw();
return 0;
}
Output:
=== Horizontal Layout ===
Button: OK at Rect(10, 10, 96, 40)
Button: Cancel at Rect(111, 10, 96, 40)
Label: Status at Rect(212, 10, 96, 40)