C++ GUI | "Qt 프레임워크" 초보자 가이드
이 글의 핵심
C++ GUI에 대해 정리한 개발 블로그 글입니다. #include <QApplication> #include <QPushButton>
첫 번째 Qt 애플리케이션
#include <QApplication>
#include <QPushButton>
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
QPushButton button("Hello Qt!");
button.resize(200, 100);
button.show();
return app.exec();
}
빌드:
# qmake 사용
qmake -project
qmake
make
# CMake 사용
find_package(Qt6 REQUIRED COMPONENTS Widgets)
target_link_libraries(myapp Qt6::Widgets)
시그널과 슬롯
#include <QApplication>
#include <QPushButton>
#include <QMessageBox>
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
QPushButton button("클릭하세요");
// 시그널-슬롯 연결
QObject::connect(&button, &QPushButton::clicked, {
QMessageBox::information(nullptr, "알림", "버튼이 클릭되었습니다!");
});
button.show();
return app.exec();
}
레이아웃
수직 레이아웃
#include <QApplication>
#include <QWidget>
#include <QPushButton>
#include <QVBoxLayout>
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
QWidget window;
QVBoxLayout *layout = new QVBoxLayout(&window);
layout->addWidget(new QPushButton("버튼 1"));
layout->addWidget(new QPushButton("버튼 2"));
layout->addWidget(new QPushButton("버튼 3"));
window.show();
return app.exec();
}
수평 레이아웃
QHBoxLayout *layout = new QHBoxLayout(&window);
layout->addWidget(new QPushButton("왼쪽"));
layout->addWidget(new QPushButton("중간"));
layout->addWidget(new QPushButton("오른쪽"));
그리드 레이아웃
QGridLayout *layout = new QGridLayout(&window);
layout->addWidget(new QPushButton("1"), 0, 0);
layout->addWidget(new QPushButton("2"), 0, 1);
layout->addWidget(new QPushButton("3"), 1, 0);
layout->addWidget(new QPushButton("4"), 1, 1);
커스텀 위젯
#include <QWidget>
#include <QPushButton>
#include <QLabel>
#include <QVBoxLayout>
class CounterWidget : public QWidget {
Q_OBJECT
private:
int count;
QLabel *label;
QPushButton *button;
public:
CounterWidget(QWidget *parent = nullptr) : QWidget(parent), count(0) {
label = new QLabel("카운트: 0", this);
button = new QPushButton("증가", this);
QVBoxLayout *layout = new QVBoxLayout(this);
layout->addWidget(label);
layout->addWidget(button);
connect(button, &QPushButton::clicked, this, &CounterWidget::increment);
}
private slots:
void increment() {
count++;
label->setText(QString("카운트: %1").arg(count));
}
};
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
CounterWidget widget;
widget.show();
return app.exec();
}
메뉴와 툴바
#include <QMainWindow>
#include <QMenuBar>
#include <QToolBar>
#include <QAction>
#include <QMessageBox>
class MainWindow : public QMainWindow {
Q_OBJECT
public:
MainWindow() {
// 메뉴 생성
QMenu *fileMenu = menuBar()->addMenu("파일");
QMenu *editMenu = menuBar()->addMenu("편집");
// 액션 생성
QAction *newAction = new QAction("새 파일", this);
QAction *openAction = new QAction("열기", this);
QAction *saveAction = new QAction("저장", this);
// 메뉴에 액션 추가
fileMenu->addAction(newAction);
fileMenu->addAction(openAction);
fileMenu->addSeparator();
fileMenu->addAction(saveAction);
// 툴바 생성
QToolBar *toolbar = addToolBar("메인 툴바");
toolbar->addAction(newAction);
toolbar->addAction(openAction);
toolbar->addAction(saveAction);
// 시그널 연결
connect(newAction, &QAction::triggered, this, &MainWindow::newFile);
connect(openAction, &QAction::triggered, this, &MainWindow::openFile);
}
private slots:
void newFile() {
QMessageBox::information(this, "알림", "새 파일 생성");
}
void openFile() {
QMessageBox::information(this, "알림", "파일 열기");
}
};
실전 예시
예시 1: 간단한 텍스트 에디터
#include <QMainWindow>
#include <QTextEdit>
#include <QMenuBar>
#include <QFileDialog>
#include <QFile>
#include <QTextStream>
class TextEditor : public QMainWindow {
Q_OBJECT
private:
QTextEdit *textEdit;
public:
TextEditor() {
textEdit = new QTextEdit(this);
setCentralWidget(textEdit);
createMenus();
setWindowTitle("간단한 텍스트 에디터");
resize(800, 600);
}
private:
void createMenus() {
QMenu *fileMenu = menuBar()->addMenu("파일");
QAction *openAction = fileMenu->addAction("열기");
connect(openAction, &QAction::triggered, this, &TextEditor::openFile);
QAction *saveAction = fileMenu->addAction("저장");
connect(saveAction, &QAction::triggered, this, &TextEditor::saveFile);
fileMenu->addSeparator();
QAction *exitAction = fileMenu->addAction("종료");
connect(exitAction, &QAction::triggered, this, &QWidget::close);
}
private slots:
void openFile() {
QString filename = QFileDialog::getOpenFileName(this, "파일 열기");
if (!filename.isEmpty()) {
QFile file(filename);
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QTextStream in(&file);
textEdit->setText(in.readAll());
file.close();
}
}
}
void saveFile() {
QString filename = QFileDialog::getSaveFileName(this, "파일 저장");
if (!filename.isEmpty()) {
QFile file(filename);
if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
QTextStream out(&file);
out << textEdit->toPlainText();
file.close();
}
}
}
};
예시 2: 계산기
#include <QWidget>
#include <QLineEdit>
#include <QPushButton>
#include <QGridLayout>
class Calculator : public QWidget {
Q_OBJECT
private:
QLineEdit *display;
double currentValue;
char currentOp;
public:
Calculator() : currentValue(0), currentOp('\0') {
display = new QLineEdit("0", this);
display->setReadOnly(true);
display->setAlignment(Qt::AlignRight);
QGridLayout *layout = new QGridLayout(this);
layout->addWidget(display, 0, 0, 1, 4);
// 숫자 버튼
for (int i = 0; i < 10; i++) {
QPushButton *btn = new QPushButton(QString::number(i), this);
connect(btn, &QPushButton::clicked, this, &Calculator::digitClicked);
int row = (9 - i) / 3 + 1;
int col = (i - 1) % 3;
if (i == 0) {
layout->addWidget(btn, 4, 1);
} else {
layout->addWidget(btn, row, col);
}
}
// 연산자 버튼
QPushButton *addBtn = new QPushButton("+", this);
connect(addBtn, &QPushButton::clicked, this, &Calculator::operatorClicked);
layout->addWidget(addBtn, 1, 3);
QPushButton *equalBtn = new QPushButton("=", this);
connect(equalBtn, &QPushButton::clicked, this, &Calculator::equalClicked);
layout->addWidget(equalBtn, 4, 3);
resize(300, 400);
}
private slots:
void digitClicked() {
QPushButton *btn = qobject_cast<QPushButton*>(sender());
QString digit = btn->text();
if (display->text() == "0") {
display->setText(digit);
} else {
display->setText(display->text() + digit);
}
}
void operatorClicked() {
QPushButton *btn = qobject_cast<QPushButton*>(sender());
currentOp = btn->text().at(0).toLatin1();
currentValue = display->text().toDouble();
display->setText("0");
}
void equalClicked() {
double secondValue = display->text().toDouble();
double result = 0;
switch (currentOp) {
case '+': result = currentValue + secondValue; break;
case '-': result = currentValue - secondValue; break;
case '*': result = currentValue * secondValue; break;
case '/': result = currentValue / secondValue; break;
}
display->setText(QString::number(result));
currentOp = '\0';
}
};
예시 3: 이미지 뷰어
#include <QMainWindow>
#include <QLabel>
#include <QMenuBar>
#include <QFileDialog>
#include <QPixmap>
#include <QScrollArea>
class ImageViewer : public QMainWindow {
Q_OBJECT
private:
QLabel *imageLabel;
QScrollArea *scrollArea;
public:
ImageViewer() {
imageLabel = new QLabel;
imageLabel->setScaledContents(true);
scrollArea = new QScrollArea;
scrollArea->setWidget(imageLabel);
setCentralWidget(scrollArea);
createMenus();
setWindowTitle("이미지 뷰어");
resize(800, 600);
}
private:
void createMenus() {
QMenu *fileMenu = menuBar()->addMenu("파일");
QAction *openAction = fileMenu->addAction("열기");
connect(openAction, &QAction::triggered, this, &ImageViewer::openImage);
}
private slots:
void openImage() {
QString filename = QFileDialog::getOpenFileName(
this, "이미지 열기", "", "Images (*.png *.jpg *.bmp)");
if (!filename.isEmpty()) {
QPixmap image(filename);
imageLabel->setPixmap(image);
imageLabel->resize(image.size());
}
}
};
자주 발생하는 문제
문제 1: Q_OBJECT 매크로 에러
증상: moc 관련 에러
원인: Q_OBJECT 매크로 사용 시 moc 실행 필요
해결법: qmake 또는 CMake 사용
문제 2: 시그널-슬롯 연결 안됨
증상: 클릭해도 반응 없음
원인: 잘못된 시그널/슬롯 이름
해결법: 컴파일 타임 체크 사용
// ❌ 런타임 체크
connect(button, SIGNAL(clicked()), this, SLOT(onClicked()));
// ✅ 컴파일 타임 체크
connect(button, &QPushButton::clicked, this, &MyClass::onClicked);
문제 3: 메모리 누수
증상: 메모리 증가
원인: 위젯 삭제 안함
해결법: 부모 위젯 설정
// ✅ 부모 설정하면 자동 삭제
QPushButton *button = new QPushButton("버튼", parentWidget);
FAQ
Q1: Qt vs 다른 GUI 프레임워크?
A:
- Qt: 크로스 플랫폼, 풍부한 기능
- wxWidgets: 네이티브 룩앤필
- GTK: 리눅스 중심
Q2: Qt 버전은?
A: Qt 6 최신 버전 사용 권장 (Qt 5도 여전히 많이 사용)
Q3: Qt Creator vs Visual Studio?
A: Qt Creator가 Qt 개발에 최적화되어 있습니다.
Q4: 상용 라이선스 필요한가요?
A: 오픈소스 프로젝트는 무료 (LGPL/GPL)
Q5: Qt Quick vs Qt Widgets?
A:
- Qt Widgets: 전통적, 안정적
- Qt Quick: 모던, 모바일 친화적
Q6: Qt 학습 리소스는?
A:
- 공식 문서: doc.qt.io
- Qt Examples
- “C++ GUI Programming with Qt” 책
같이 보면 좋은 글 (내부 링크)
이 주제와 연결되는 다른 글입니다.
- C++ 크로스 플랫폼 GUI | Qt 기초 완벽 가이드 [#36-2]
- C++ 현대적인 C++ GUI: Dear ImGui로 디버깅 툴·대시보드 만들기 [#36-1]
- C++ 디자인 패턴 | “싱글톤/팩토리/옵저버” 실전 가이드
관련 글
- C++ 크로스 플랫폼 GUI | Qt 기초 완벽 가이드 [#36-2]
- C++ 현대적인 C++ GUI: Dear ImGui로 디버깅 툴·대시보드 만들기 [#36-1]