VEDA 어느덧 한달차를 향해 달려가고 있는 지금!! 4주차에서는 Qt를 배우며 한 주가 지나갔습니다. 처음 써보는 Tool이기도 하고 전공 과정에서 Android를 개발하며 헤맸던 기억이 있어 걱정했지만 나름 재미있었습니다.
그러면 4주차 기록 시작하겠습니다!! 🤨
Qt를 배우기전 build,make에 대해 먼저 얘기를 해보도록 하겠습니다.
qt에서는 qmake , cmake 두가지 빌드를 통해서 컴파일, 실행 파일 생성의 단계로 이어지고 있습니다.
CMakeLists.txt를 살펴 보겠습니다.
cmake_minimum_required(VERSION 3.16) //CMake 최소 버전 명세
project(ConsoleApplication LANGUAGES CXX) // 프로젝트 이름과 사용된 언어
//CXX -> C++
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 17) //C++17 문법을 쓰겠다.
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core)
//패키지를 찾아서 경로를 가져온다.
//ConsoleApplication이라는 실행 파일을 생성.
//소스 파일로는 main.cpp를 포함
add_executable(ConsoleApplication
main.cpp
)
//어떤 라이브러리들을 link 해서 사용하겠다.
target_link_libraries(ConsoleApplication PRIVATE Qt${QT_VERSION_MAJOR}::Core)
//표준 설치 디렉토리 변수 include
include(GNUInstallDirs)
//build 실행 파일을 설치할 위치를 지정.
install(TARGETS ConsoleApplication
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
추가할 파일, 라이브러리, package를 명시해둔 것을 확인 할 수 있습니다.
AUTOUIC: .ui 파일을 자동으로 처리 (Qt Designer GUI 파일)AUTOMOC: Q_OBJECT 매크로가 있는 클래스에 대해 자동으로 MOC 처리AUTORCC: .qrc 리소스 파일을 자동으로 처리REQUIRED ON은 반드시 C++17을 지원하는 컴파일러를 사용해야 함을 의미최근은 CMakeLists.txt를 이용한 build로 넘어가는 추세이니 Makefile 문법은 다음에 따로 정리를 하도록 하겠습니다.
qmake를 이용한 프로젝트 빌드는 확장자가 .pro이며 파일명은 프로젝트 이름을 사용합니다. 비교적 cmake에 비해 문법이 간단하나 일단은 넘어가겠습니다.
Qt는 C++ 언어를 기반으로 한 크로스플랫폼 애플리케이션 프레임워크입니다. 한 번 개발하면 다양한 운영체제(Windows, Linux, macOS, Android, iOS 등)에서 동일하게 실행할 수 있는 프로그램을 만들 수 있도록 지원합니다.
주요 특징
C++ 기반으로 하드웨어 접근성이 뛰어나고, 고성능 애플리케이션 개발에 유리합니다.
주로 GUI(그래픽 사용자 인터페이스) 프로그램 개발에 활용되지만, 네트워크, 데이터베이스, 멀티미디어, 스레드 등 다양한 기능 모듈도 제공합니다.
QML(Qt Markup Language): JavaScript 기반의 선언형 언어로, 직관적이고 반응형 UI를 개발할 수 있도록 지원합니다. 프론트엔드를 QML로, 백엔드를 C++로 구현해 연동하는 방식이 많이 쓰입니다.
Signal과 Slot: 객체 간 통신을 쉽게 구현하는 Qt만의 기본 메커니즘으로, 이벤트 기반 프로그래밍(버튼 클릭 등)을 효율적으로 처리할 수 있습니다.
풍부한 위젯과 컨테이너: 기본 버튼, 입력창 등 다양한 UI 컴포넌트와 그래픽, 네트워크, DB 등 광범위한 개발 지원
오픈소스 및 상용 라이선스: 무료로 사용할 수 있으며, 상업적 개발에서도 제한적 사용이 가능합니다
Signal과 Slot은 이벤트 기반 프로그래밍(버튼 클릭 등)을 효율적으로 처리할 수 있는 메커니즘입니다.
Connect(시그널 발생 오브젝트 , Signal[발생 종류],slot함수가 있는 인스턴스(클래스), Slot[호출할 함수])
connect를 통해서 Sigal과 slot을 연결시킨다면 버튼 클릭시 호출되는 함수 등을 설정할 수 있습니다.
slot-> eventhandler : click 이벤트가 발생하면 signal을 보내게 되는데 이때 signal을 처리하는 이벤트 핸들러가 Slot의 개념입니다.
signal : 어떤 상황에 발생하는 이벤트
파이프라인 개념 ⇒ 하나의 시그널이 여러 Slot을 호출 할 수 있고 여러개의 Signal이 하나의 Slot를 호출 할 수 있다.
⭐이때 Signal과 SLOT은 포인터로 연결되는 개념 → connect에서는 인자를 매개변수로 넣을 수 없고 signal과 slot 사이에는 인자를 보낼 수도 있습니다.
⭐️Signature 와 slot의 인자 매칭 기본 원칙
이벤트 핸들러에서 connect로 연결할 때 Signal과 SLOT함수 사이의 인자를 일치시켜야 한다.
인자 개수와 타입이 정확히 일치하거나 SLOT이 더 적은 인자를 받아야 연결된다!!
connect(&myObject, SIGNAL(valueChanged(int)),
this, SLOT(setValue(int)));
connect(&myObject, &SignalSlot::valueChanged,
this, &Widget::setValue);
cnt1 = new Counter("counter 1");
cnt2 = new Counter("counter 2");
connect(cnt1, SIGNAL(valueChagned(int)), cnt2, SIGNAL(valueChagned(int)));
connect(cnt2, SIGNAL(valueChagned(int)), cnt2, SLOT(setValue(int)));
//이러면 cnt1의 발생 Signal이 cnt2의 Signal과 연결Qt에서 위젯 클래스는 모두 QObject를 상속받고 있으며 복사 생성자와 복사 대입 연산자가 삭제되어 있습니다.
또한 스마트 포인터를 통해 해당 인스턴스 내에서 new 키워드를 통해 동적 할당된 메모리는 자동으로 소멸되어 별도로 delete를 하지 않아도 됩니다.
그러면 Widget의 사용법을 하나씩 살펴보겠습니다.
다양한 위젯을 활요해보았지만 주요 위젯들만 언급하겠습니다.
Qpushbutton/*
btn_frame을 class의 private으로 사용해 클래스 내에서 사용할 수 있게 만들고
클릭 될때마다 지정을 변경해준다.
*/
void Widget::btn_released()
{ QPushButton *clicked = qobject_cast<QPushButton*>(sender());
btn_frame -> setWidget(clicked);
qDebug("Button Relased");
}
...
...
//생성자
//button 동작 함수 연결 -> clicked , pressed, realeased
connect(btn[0], &QPushButton::clicked, this, &Widget::btn_click);
connect(btn[0], &QPushButton::pressed, this, &Widget::btn_pressed);
connect(btn[0], &QPushButton::released, this, &Widget::btn_released);
//근데 connect가 그냥 이벤트 핸들러를 변경하는 건줄 알았는데 connect 동일한 것을
//여러번 쓰면 동일함수가 여러번 호출
QLable 다재다능해서 은근히 많이 쓰임QLabel : 어플리케이션 상에서 텍스트 또는 이미지 표시
QLCDNumber : 디지털 시계와 같은 형태로 숫자 표시
QPixmap pix = QPixmap("resource"); //이미지 로드 이미지 객체 pix 생성
lbl[1] -> setPixmap(pix);
lcd[1] = new QLCDNumber(5, this); //여기서 5는 디스플레이에 표시할 수 있는 자리수
lcd[1]->display("10:34"); //-> 시간 설정
lcd[1]->setGeometry(150, 140, 200, 100);
lcd[1]->setSegmentStyle(QLCDNumber::Filled); // 테두리 Bold
Label은 text나 이미지를 표시할 때 주로 사용됩니다.
QScrollArea : 윈도우 상에 위젯이 모두 표시되지 않을 경우 스크롤 바로 이동할 수 있는 기능QImage 를 사용하는 이유 ⇒ 리소스 직접 로딩시 디버깅에 용이
QImage image;
QScrollArea *area;
QLabel *lbl = new QLable(this);
image = QImage(":resource/~~");
lbl -> setPixmap(QPixmap::fromImage(image));
area = new QScrollArea(this);
area-> setWidget(lbl);
area-> setBackgroundRole(QPalette::Dark);
area ->setGeometry(0,0,image.width(),image.height());
기본적으로 내부에 테두리와 여백을 가지고 있어서 이미지 사이즈에 맞게 크기를 설정해도 규격에 맞지 않을 수도 있다.
QWidget 클래스는 paintEvent( ) virtual 함수를 이용해 위젯 영역에 텍스트, 도형(선, 원,사각형 등), 이미지를 랜더링 할 수 있는 기능을 제공한다.
또한 QWidget 클래스의 update() 멤버 함수를 호출하면 paintEvent ( ) 함수를 호출 할 수 있다
근데 왜 호출 되냐?
paintEvent()를 호출해서 그려줍니다.Widget이 생성되고 나면, Qt는 해당 위젯을 화면에 표시하기 위해 그려야 한다고 판단하고 자동으로 paintEvent()를 트리거합니다.표시될 때 내부적으로 paintEvent()를 호출하는데 이때 가상함수를 이용해서 오버라이딩 함수를 실행한다.
main 에서 show()를 수행하고 나면 Qt 이벤트 루프가 활성화 된다 이후에 paintEvent() 호출
Qwidget 내에서 SetGeometry() 멤버 함수를 사용하면 GUI상에서 x,y 절대 좌표로 위젯을 배치
이는 윈도우의 크기가 변경될 때 위젯의 위치가 변경 되지 않는다.
⇒ 레이아웃 활용 ( 동적으로 GUI상의 위젯의 크기 동적 변경 가능)
| 클래스 | 설명 |
|---|---|
| QHBoxLayout | 위젯을 가로 방향으로 배치 |
| QVBoxLayout | 위젯들을 세로 방향으로 배치 |
| QGridLayout | 위젯을 그리드 또는 바둑판 스타일로 배치 |
| QFormLayout | 위젯을 2열로 배치하는 형식 |
(실습하면서 윈도우 사이즈에 맞춰서 동적으로 위젯 사이즈가 조정되기는 하나 버튼의 default 사이즈는 존재하는 듯?)
Listwidget과 ListView 의 차이 : 데이터 삽입, 수정, 삭제 방식이 차이
-> Widget이라는 단어를 사용한 클래스는 직접 데이터를 삽입, 수정, 삭제할 수 있는 멤버 함수를 제공한다.
-> ListView와 같이 View로 끝나는 위젯 클래스들은 멤버함수를 통해 삽입, 수정, 삭제하지 않고 Model 클래스라는 매개체를 이용한다.
Model ↔ View : Delegate를 사용해 이벤트를 처리할 수 있습니다.
Model에서는 여러 멤버 함수를 통해 데이터를 처리할 수 있습니다.
model -> view는 layout에 addWidget을 해서 setLayout() 을 해야 화면에 표시되고 보임.
QStandardItemModel *model = new QStandardItemModel(0, 3); //테이블이나 트리 모델
//초기 행 0 열 3
//각 열의 헤더 지정
model->setHeaderData(0, Qt::Horizontal, QObject::tr("Subject")); //
model->setHeaderData(1, Qt::Horizontal, QObject::tr("Description"));
model->setHeaderData(2, Qt::Horizontal, QObject::tr("Date"));
//각 행의 라벨 설정.
model->setVerticalHeaderItem(0, new QStandardItem("Col 1"));
model->setVerticalHeaderItem(1, new QStandardItem("Col 2"));
//데이터 설정.
model->setData(model->index(0, 0), "Monitor");
model->setData(model->index(0, 1), "LCD");
model->setData(model->index(0, 2), QDate(2030, 10, 4));
//모델을 view에 설정
QTableView *table = new QTableView();
table->setModel(model);
//layout에 view를 넣고 setLayout()해야 화면에 표시
QVBoxLayout *lay = new QVBoxLayout();
lay->addWidget(table);
setLayout(lay);
또한 TableWidgetd에서 paintEvent 와 view를 이용해서 커스텀 디자인된 header를 만들 수도 있습니다.
1. column 헤더에 QCheckBox 삽입
//checkboxHeader.h
class CheckBoxHeader : public QHeaderView
{
Q_OBJECT
public:
CheckBoxHeader(Qt::Orientation orientation,
QWidget* parent = nullptr);
bool isChecked() const { return isChecked_; }
void setIsChecked(bool val);
signals:
void checkBoxClicked(bool state);
protected:
void paintSection(QPainter* painter,
const QRect& rect,
int logicalIndex) const;
void mousePressEvent(QMouseEvent* event);
private:
bool isChecked_;
void redrawCheckBox();
};
//checkboxheader.cpp
void CheckBoxHeader::paintSection(QPainter* painter,
const QRect& rect,
int logicalIndex) const
{
painter->save();
QHeaderView::paintSection(painter, rect, logicalIndex);
painter->restore();
if (logicalIndex == 0) //첫번째 열에만 헤더박스를 추가하도록
{
QStyleOptionButton option;
option.rect = QRect(1,3,20,20);
option.state = QStyle::State_Enabled | QStyle::State_Active;
if (isChecked_)
option.state |= QStyle::State_On;
else
option.state |= QStyle::State_Off;
option.state |= QStyle::State_Off;
//실제 체크박스를 그리는 함수.
style()->drawPrimitive(QStyle::PE_IndicatorCheckBox,
&option,
painter);
}
}
void CheckBoxHeader::mousePressEvent(QMouseEvent* event)
{
Q_UNUSED(event)
setIsChecked(!isChecked());
emit checkBoxClicked(isChecked()); //signal 전송.
}
void CheckBoxHeader::redrawCheckBox()
{
viewport()->update();
}
void CheckBoxHeader::setIsChecked(bool val)
{
if (isChecked_ != val)
{
isChecked_ = val;
redrawCheckBox();
}
}
2. Widget.cpp에서 활용
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
ui->tableWidget->setRowCount(5);
ui->tableWidget->setColumnCount(2);
CheckBoxHeader* header = new CheckBoxHeader(Qt::Horizontal, ui->tableWidget);
ui->tableWidget->setHorizontalHeader(header);
//시그널 - 슬롯 연결
connect(header, &CheckBoxHeader::checkBoxClicked,
this, &Widget::checkBoxClicked);
QStringList nameList;
nameList << "Notebook" << "Mobile" << "Desktop" << "Keyboard" << "Monitor";
for(int i = 0; i < 5 ; i++)
{
ui->tableWidget->insertRow(i);
QTableWidgetItem *dateItem = new QTableWidgetItem("2025.05.07");
//dateItem 내에 체크박스 설정
//설정 안하면 체크박스 안생김
dateItem->setCheckState(Qt::Checked);
//안에 내장되어있고 visible 속성이 있는 듯.
ui->tableWidget->setItem(i,0, dateItem);
ui->tableWidget->setItem(i,1, new QTableWidgetItem(nameList.at(i)));
}
}
//slot 함수 : 헤더 state에 따라 col의 모두 바꾸는 함수.
void Widget::checkBoxClicked(bool state)
{
for(int i = 0 ; i < 5 ; i++)
{
QTableWidgetItem *item = ui->tableWidget->item(i, 0);
if(state)
item->setCheckState(Qt::Checked);
else
item->setCheckState(Qt::Unchecked);
}
}
map과 같은 애플리케이션을 윈도우에 표시하기 위해서 Qt Graphics View Framework를 제공한다.
→ 내부 구현 알고리즘으로 BSP(Binary Space Partitioning)을 사용한다.
| 요소 | 설명 |
|---|---|
| QGraphicsView | 시각적으로 표시가 가능한 영역 |
| QGraphicsScene | 논리적인 영역 View 영역 안에 표시된다. |
| QGraphicsItem | 클래스 오브젝트 상에 표시 ex) 맵상에 표시되는 오브젝트 개념 |
| QPainter와 같이 paint() 함수를 제공한다. |
예시
신기 method :GraphicsItem을 상속받는다 이때 내부에 구현된 collidingItems를 사용해 shape Item끼리 부딪혔을 때 별도의 함수 호출 가능
이외에도 QChart,QtDataVisualization, State Machine 등의 다양한 요소들에 대해서 간략하게 배웠지만 예제를 이해하기에는 기초 함수들의 파악도 되어있지 않아 실제로 사용하고 구현해보면서 이해를 하는게 낫겠다는 생각이 들었다.
Multi- Media 호출 또한 Qt 내부의 다양한 함수와 이미 구현된 클래스를 통해 간단하게 불러올 수 있었다. 한화 비전의 영상 솔루션을 위해서라면 미리미리 카메라 입력 처리와 디자인을 연습해보는 것도 좋을 것 같다는 생각이 들었다.

camera device는 microphone device가 함께 제공됨 → 따라서 QAudio Output을 연결해야 한다.
예제 코드는 생각보다 길어서 넘어가겠습니다
Result

ㅎㅎㅎㅎ
중간에 build 문제가 생겨서 헤맸다. D drive에거 작업하던 것들을 C로 옮기면서 Project의 Build 설정이 바뀌어 오류를 겪었고 디렉토리 명에 한글이나 White Space가 들어간 경우 Directory 참조 오류가 발생할 수 있음을 확인했다. 디렉리나 파일명은 반드시 한글, 공백 없이 작성하자!!
새로 만든 .ui 디자인으로 기존의 코드 디자인 변경하기
CMakeList.txt 에 새로 생성한 ui 파일을 추가
⇒ 이후 ui_”파일 이름 “.h가 생긴 것을 확인 할 수 있다.
Animation
⭐ Qt에서 QAbstractAnimation 인스턴스는 하나의 그룹에만 속할 수 있다 → 따라서 두 번째 그룹에 추가할 때 이전 그룹에서 제거 되어 애니메이션 동작이 발생하지 않는다.
일주일만에 Qt를 다 배우기는 조금 난감하긴 했다. 그래도 5주차에는 3일간 심화 실습을 통해 구현을 진행하니 그 때 추가적으로 학습을 이어나가면서 이해해보도록 하겠습니다...
그렇다면 다음 주도 파이탱~~🥧
🍜신마웨이
부끄러운 마라탕 전문가로써.. 9000원에 점심 마라탕 특선(?)이 있는 신기한 식당이었다. 살짝쿵 땅콩 소스의 비중이 너무 큰 탓에 내 입맛에는 다소 아쉬웠다.
평점 :⭐️⭐️⭐️+0.6 [5점 만점]
🍛싸다 김밥 -뱅뱅사거리점
싸..싸다 김밥이 Cheap인줄 알았지만 김밥을 싸다라는 것은 처음 안 사실.
이미 6회차 동기들에게 든든한 국밥 같은 식당이었지만 4주 동안 처음 가봤다. 흠.. 단백질이 살짝 아쉬워 계란볶음밥에 계란후라이를 추가해먹은 미친 남자랄까? 콩나물 무침이 맛있어 다른 메뉴들도 도전해보고 싶어지는 그런 식당 .
평점 :⭐️⭐️⭐️+0.7 [5점 만점]
여기까지 오신 독자 여러분들 5주차에 다시 만나 뵈기를 고대하면서 Thank you~~💋