
기본 레이아웃 자체가 위 구조로 되어있는 클래스라 편하게 메뉴구성, 툴바구성, 필요한 위젯 배치가능.
맨 아래에는 status bar를 가져서 필요한 내용들을 갖다붙이기만 하면 된다.
그 위젯들을 직접 만들 필요가 없음.
윈도우창 열고 어플리케이션 수행되는 것에 별도의 창을 통해 다른 기능을 수행하는게 일반적.
바탕화면 → 아이콘 클릭 → 창 뜨기 → 어플리케이션 수행
메모장처럼 단순하게 편집기를 메인윈도우 클래스를 통해 만들어보도록 하자.
메뉴바 툴바 스테터스바 클래스를 인스턴스를 생성한 후 메인윈도우 클래스에 설정
이걸 그냥 넣으면 기본적인 배치로 메인윈도우에 들어가게 된다.
큐티에서 제공하는 메인윈도우를 상속받아서 프로젝트 만들 때 QTEditor 클래스가 만들어짐.
텍스트에딧 위젯을 만들어서 메인윈도우 중앙에 배치.
QMenu 클래스 이용해서 메뉴바에 등록하면 메뉴바의 메뉴구성을 할 수 있따.
메뉴바 클래스에 메뉴 등록.
메뉴바에 등록된 메뉴 객체마다 시그널을 발생시켰을 때(클릭 트리거링 등) 슬롯함수로 등록해서 시그널 처리해도 되지만, 큐티에서는 QAction이라는 클래스를 제공.
QAction 클래스를 이용하여 메뉴, 툴바 등 사용자 이벤트에 대한 처리를 하나의 클래스로 묶어서 등록해두면 사용자의 입력된 데이터가 있을 때 메뉴 혹은 툴바에서 동일하게 액션을 처리할 수 있도록 만들어짐.
메뉴, 툴바, 단축키 등에서 실행할 수 있는 '동작(기능)'을 캡슐화한 클래스
메뉴 바 (QMenu)
툴바 (QToolBar)
단축키 (QKeySequence)
마우스 오른쪽 메뉴 (context menu)
💡 이 모든 곳에 동일한 "동작"을 중복 없이 한 번만 정의하고 공유하려고 사용하는 게 QAction이야.
QAction *openAction = new QAction(QIcon(":/icons/open.png"), tr("&Open"), this);
openAction->setShortcut(QKeySequence::Open);
openAction->setStatusTip("Open an existing file");
connect(openAction, &QAction::triggered, this, &MainWindow::openFile);
| 코드 | 설명 |
|---|---|
QAction(...) | 액션 생성 (아이콘 + 이름 + 부모) |
setShortcut(...) | 단축키 지정 (Ctrl+O 등) |
setStatusTip(...) | 상태표시줄에 표시할 설명 |
connect(...) | 액션이 실행되었을 때 함수 호출 연결 |
| 사용 위치 | 예시 |
|---|---|
| 메뉴 바 | menu->addAction(openAction); |
| 툴바 | toolbar->addAction(openAction); |
| context menu | menu.addAction(openAction); |
| 기타 위젯 | widget->addAction(openAction); 가능 (단축키 등록 등) |
| 메서드 | 설명 |
|---|---|
setShortcut(QKeySequence) | 단축키 설정 |
setIcon(QIcon) | 아이콘 설정 |
setText(QString) | 메뉴나 툴바에 표시될 이름 |
setCheckable(true) | ON/OFF 가능한 토글 액션 만들기 |
setEnabled(false) | 비활성화 상태로 만들기 |
setStatusTip() | 상태바에 표시될 텍스트 지정 |
| 시그널 | 설명 |
|---|---|
triggered() | 액션이 실행되었을 때 발생 (클릭, 단축키 등) |
toggled(bool) | 체크 가능한 액션에서 상태가 바뀌면 발생 |
QAction *boldAction = new QAction("Bold", this);
boldAction->setCheckable(true);
connect(boldAction, &QAction::toggled, this, [](bool checked){
qDebug() << "Bold:" << (checked ? "ON" : "OFF");
});
이제 new하면 새로운 파일이 열리도록 해보고싶다.
기존 파일을 여는 작업 수행해보자.
메뉴바 밑에 툴바를 만들고 자주쓰는 것들을 담아놓고 쓸 수 있게 만들고 있다.
큐티에서 툴바를 어떻게 생성해서 이용할 수 있는지를 정리해보자.
QToolBar 클래스
버튼으로 구성된 이동 가능한 패널 제공
QMainWindow 클래스나 그 서브 클래스에서만 사용 가능.
툴바에 항목 추가 : addAction() / addWidget()
중앙 분리선 추가 : addSeperator()
툴바의 버튼 스타일 설정 : setToolButtonStyle()
툴바는 팔레트처럼 분리 가능 : Qt에서 자동 지원.
아니면 툴바버튼 위치가 메뉴바 바로 밑에 기본 레이아웃을 가져서, 거기에 고정형으로 사용도 가능함.
버튼스타일을 직접 선택할 수 있다.
QToolBar *fileToolBar = addToolBar("&File");
fileToolBar->setToolButtonStyle(Qt::ToolButtonIconOnly);
fileToolBar->addAction(actionNew);
fileToolBar->addAction(actionOpen);
fileToolBar->addAction(actionSave);
fileToolBar->addAction(actionSaveAs);
fileToolBar->addSeparator();
fileToolBar->addAction(actionQuit);
QStatusBar
메인 윈도우 맨 아래 수평으로 위치
StatusBar에 어플리케이션의 상태를 출력되도록 할 수 있다.
일시적으로 메시지를 잠시 보이고자할때는 showMessage() 멤버함수로 출력하게할수도 있고,
위젯을 만들어서 등록해주면 위젯에 등록된 내용을 앱종료할 때까지 계속 유지하도록 만들수있음
페이지정보, 라인정보 등 일시적인 메시지로 띄워줄수도 있음.
QStatusBar *statusbar = statusBar();
QLabel* statusLabel = new QLabel("Qt Editor", statusbar);
statusLabel->setObjectName("StatusLabel");
statusbar->addPermanentWidget(statusLabel);
statusbar->showMessage("started",150);
별도로 statusbar를 new 생성하는게 아니라, statusBar() 라는 메인윈도우의 함수를 호출하면 메인윈도우 내의 statusbar를 켜게됨. new statusBar() 하지 않도록 하자.
이제 편집메뉴를 만들어보자
메뉴바에 Edit 메뉴와 Help 메뉴 추가하고 기능 구현
Edit의 경우 Undo/Redo | Copy | Cut | Paste 기능들이 QTextEdit 내에 존재하기 때문에 간단하게 슬롯함수 연결이 가능.
Help의 경우, 각각을 메시지박스로 출력하게 진행.
표준 입출력 스트림 방식이 큐티에도 이용하고 있는 방식이다.
교재 13장이 파일입출력하기 앞서서 스트림이라는 것에 대한 소개하고 있다.
교재13장
특정 데이터를 read/write하기 쉽게 만들어놓은 일련의 자료형
QByteArray라는 qt서 쓰는 배열형태가 존재함.
여기에 Qdatastream, Qtextstream이라는 클레스를 활용해서 bytearray 읽기쓰기 가능함.
텍스트 형태는 텍스트 스트림으로, 바이너리형태는 데이터스트림을 통해 입출력이 가능하다.
QByteArray encoding()
{
quint32 value1 = 123;
quint8 value2 = 124;
quint32 value3 = 125;
QByteArray outData;
QDataStream outStream(&outData, QIODevice::WriteOnly);
outStream << value1;
outStream << value2;
outStream << value3;
qDebug() << "outData size : " << outData.size() << " Bytes";
return outData;
}
다양한 모드(C,C++에서 파일 오픈 시 어떤 모드로 열지를 선택해야햇던것처럼 동일)들을 활용할 수 있다.
이 enum 타입을 상속받아 QIODevice를 만들고, QFile을 만들게된다.
데이터스트림 형태로 (우리가 보통 이진 데이터를 사용할 때쓰는클래스) 출력 연산자같이 연산자를 통해 데이터를 내보내면 하나의 스트림으로 모여지게 된다.
하나의 사이즈 등등
그 객체를 통해 사용할 수 있다.
입출력하는 방법이 큐티 파일 입출력 또한 표준 입출력 수행하는 것과 동일하게 구성되어있다.
큐티 데이터스트림, 텍스트스트림 또한 << 연산자로 입력 출력 가능하다
QByteArray, QTextStream, 그리고 Qt의 입출력 구조를 활용해서 바이트 스트림 입출력 시뮬레이션을 하고 있어. 전체 흐름과 Qt 입출력 모델을 기반으로 코드를 구조적으로 설명해줄게!| 구성 요소 | 설명 |
|---|---|
QIODevice | I/O 동작의 기본 추상 클래스 (파일, 메모리, 네트워크 등 전부 이 기반) |
QFile, QBuffer, QTcpSocket 등 | QIODevice를 상속한 실제 입출력 객체들 |
QTextStream, QDataStream | 스트림 객체, QIODevice에 연결해서 데이터를 읽고 씀 |
QByteArray writeData(QByteArray _data)
{
QByteArray temp = _data;
QByteArray outData;
QTextStream outStream(&outData, QIODevice::WriteOnly); // QTextStream ← QIODevice 역할을 QByteArray가 함
여기서 outData는 메모리상의 버퍼(QByteArray) 역할
QTextStream이 이 QByteArray에 연결되어 있고, 쓰기 전용임
즉, outStream은 outData라는 "장치"에 문자 단위로 텍스트를 출력하는 스트림이야
for(qsizetype i = 0 ; i < temp.size() ; i++) {
outStream << temp.at(i); // 문자 하나씩 출력
}
outStream.flush();
return outData;
}
flush()는 스트림 버퍼를 실제 outData에 반영
QByteArray를 문자 단위로 스트림을 통해 복사하는 구조
void readData(QByteArray _data)
{
QTextStream outStream(&_data, QIODevice::ReadOnly);
QByteArray inData;
for(qsizetype i = 0 ; i < _data.size() ; i++)
{
char data;
outStream >> data; // 문자 단위로 읽기
inData.append(data);
}
qDebug("READ DATA : [%s]", inData.data());
}
_data는 메모리 버퍼 역할
QTextStream을 연결해서 읽기 전용으로 사용
operator >>를 통해 문자 하나씩 읽어오고 inData에 저장
최종적으로 복원된 데이터를 출력
QTextStream >> char 사용은 공백(스페이스) 무시 특성을 가짐
그래서 "Hello world." → "Hello"만 복원됨
스트림 입출력은 기본적으로 공백을 구분자로 인식함
outStream << temp; // 그냥 한 번에 출력
QString str;
inStream >> str; // 이건 단어 하나만 읽음 (스페이스 기준)
QString line = inStream.readAll(); // 전체 텍스트 읽기
QTextStream 사용 흐름QIODevice(ex: QByteArray, QFile, QBuffer)를 만들고
QTextStream을 그에 연결해서
텍스트 기반의 입출력을 수행
QByteArray buffer;
QTextStream stream(&buffer, QIODevice::WriteOnly);
stream << "Hello, world!";
| 개념 | 설명 |
|---|---|
Qt 입출력의 핵심은 QIODevice 기반으로 작동 | QTextStream은 그 위에서 입출력을 처리하는 상위 계층 |
QByteArray를 가상 입출력 장치처럼 쓴 구조 | 파일이나 네트워크 대신 메모리를 입출력 대상으로 사용한 것 |
| 단어별 입출력은 공백 기준이라 문제 발생 가능 | 문자열 전체를 다루거나 readAll()을 써야 함 |
궁금하면 QDataStream으로 바이너리 입출력하는 구조도 설명해줄게!
혹시 이걸로 파일 저장/로드 같은 기능 구현하려는 중이야? 😊
#include "widget.h"
#include <QApplication>
#include <QCoreApplication>
#include <QIODevice>
#include <QTextStream>
#include <QDebug>
QByteArray writeData(QByteArray _data)
{
QByteArray temp = _data;
QByteArray outData;
QTextStream outStream(&outData, QIODevice::WriteOnly);
for(qsizetype i = 0 ; i < temp.size() ; i++) {
outStream << temp.at(i);
}
outStream.flush();
return outData;
}
void readData(QByteArray _data)
{
QTextStream outStream(&_data, QIODevice::ReadOnly);
QByteArray inData;
for(qsizetype i = 0 ; i < _data.size() ; i++)
{
char data;
outStream >> data;
inData.append(data);
}
qDebug("READ DATA : [%s]", inData.data());
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QByteArray retData = writeData("Hello world."); // 12 Bytes
qDebug() << "WRITE DATA size : " << retData.size() << " Bytes";
readData(retData);
return a.exec();
}
파일 입출력
#include <QCoreApplication>
#include <QFile>
#include <QString>
#include <QDebug>
#include <QTextStream>
void write(QString filename)
{
QFile file(filename);
if (!file.open(QFile::WriteOnly | QFile::Text))
{
qDebug() << "Open fail.";
return ;
}
QTextStream out(&file);
out << "Write Test";
file.flush();
file.close();
}
void read(QString filename)
{
QFile file(filename);
if (!file.open(QFile::ReadOnly | QFile::Text))
{
qDebug() << "Open fail.";
return ;
}
QTextStream in(&file);
qDebug() << in.readAll();
file.close();
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QString filename = "C:\\Users\\1-15\\Desktop\\sihwan\\Qt\\Editor\\main.cpp";
write(filename);
read(filename);
return a.exec();
}
파일 권한 확인을 위해 QFileInfo 라는 클래스가 있다.
파일 정보, 경로, 디렉터리 정보, 시간정보 등을 포함함.
파일을 저장할때는 파일의 쓰기권한이 있는지를 확인해야함
반대로 읽어올때는 읽기권한이 있는지 확인하도록.
파일명을 보관하는 변수하나 필요할것.
JSON p238
XML로 나타낼 내용을 JSON으로 나타낼수도있다.
JSON은 정보를 빠르게 전송할 수 있는 데이터타입 형태
좀 더 간결하고 분석하기 용이한 패턴임.(요즘에 많이 쓴다)
XML의 태그 대신, “ 와 [ ] 기호를 써서 데이터 저장 공간이 절약됨.

QT에서는 json 모듈에 대해 bool, double, string, array, object, null 6가지 타입 제공.
✔️ 맞아!QJsonDocument로 JSON 파일을 읽어들이면 전체 문서 객체가 된다.
QJsonDocument는 루트 전체(JSON 전체)를 표현하는 객체고, 루트가 { ... }이면 .object()로, 루트가 [ ... ]이면 .array()로 접근할 수 있어.🔸 거의 맞아! (단, “여러 개”는 약간 보완)이후 jsonResponse.object()를 통해 JSON 내 JSON 객체들({key: value})을 받아온다. (여러 개)
jsonResponse.object()는 루트가 객체일 때 그 하나의 JSON 객체를 리턴해.
그 안에 "key": value 쌍이 여러 개 들어있는 형태지.
즉, “루트 객체 안의 여러 key-value 쌍을 하나의 QJsonObject로 받는다”가 더 정확해.
그 JSON 객체들은 jsonObj["time"] 하는 식으로 key로 접근해서 value를 꺼낼 수 있다.
✔️ 정확함!
리턴 타입은 QJsonValue
jsonObj["time"].toString(), toBool(), toInt() 등으로 실제 값을 얻을 수 있어.
JSON 배열 객체는 배열로 만들어주기 위해 .toArray()를 붙인다.
QJsonArray jsonArray = jsonObj["properties"].toArray();
✔️ 정확함!
"properties"가 배열이라면 → jsonObj["properties"]는 QJsonValue
.toArray()를 호출해서 QJsonArray로 변환 → foreach 등으로 순회 가능
QJsonDocument doc = QJsonDocument::fromJson(data); // JSON 전체 문서를 객체로
QJsonObject rootObj = doc.object(); // 루트가 객체면 .object()
QString time = rootObj["time"].toString(); // 키로 값 추출
QJsonArray props = rootObj["properties"].toArray(); // 배열이면 .toArray()
QJsonDocument는 JSON 전체 문서를 표현하고,
.object()로 루트 객체를 가져온 후,
"key"로 값을 꺼내고,배열 값은
.toArray()로QJsonArray로 변환해서 다룬다.
json에 폰트종류, 컬러속성을 미리 정의해둔 뒤, 해당 폰트종류와 컬러속성을 디폴트 값으로 사용하도록 할 수 있음.