티스토리 뷰

LCD Number widget

Line edit

progress bar

Tab widget

4개 위젯을 받아서 코드구현 / 디자이너 두가지로 실습을 진행한다

cmake-widget-.ui x로 프로젝트를 새로 만든다

탭바 탭위젯 등 조금씩 다른데, 탭위젯은 조금 작은 화면에 대해 하는듯

이제 구현해야한다 기본은 아래와 같으니… 중괄호 펼쳐서 생성자 내용 작성해야한다

위젯을 만들기 전에, 각각의 위치와 사이즈를 미리 정해두는 것이 좋다 안겹치게…

(QLCDNumber에 커서를 깜빡이게 두고 F1을 누르면 help창이 뜨네)

Mode자리에 2가 Dec, 10진수인 것을 알 수 있다

Copy
    lcd = new QLCDNumber(2, this);
    lcd->setGeometry(50, 30, 200, 100);
    lcd->setSegmentStyle(QLCDNumber::Filled);
    lcd->display(10);

에디터는 초기값을 지정할 수 있다

또한, 비밀번호는 입력해도 가려서 보여주는 등, 설정을 할 수 있다.

화면을 보여주는 것이므로, echo가 들어가는 함수를 사용하면 된다.

(아까처럼 QLineEdit에 커서 두고 F1으로 문서를 볼 수 있다)

Copy
    edit = new QLineEdit("초기값", this);
    edit->setGeometry(50, 150, 200, 30);
    edit->setEchoMode(QLineEdit::Normal);

 

글자 넘어가는거 개열받네

프로그래스바는 오리엔테이션, 즉 가로/세로를 지정할 수 있다

앗 이번엔 Qt 네임스페이스에 있는 오리엔테이션을 입력해주어야 한다

참고로, 노란색(위처럼 빈 노란색과, 채워진 노란색)/빨간색/파란색 차이로 상수 등이라는 힌트를 알 수 있다

(검색해봐약ㅆㄱ다)

Copy
    progress = new QProgressBar(this);
    progress->setGeometry(50, 200, 200, 30);
    // progress->setMinimum(0);
    // progress->setMaximum(100);
    progress->setRange(0, 100); // min max 한꺼번에 지정
    progress->setOrientation(Qt::Horizontal);
    progress->setInvertedAppearance(false);
    progress->setValue(20);

탭위젯은 컨테이너이기 때문에 이 3개만 만들고 실행해보면 화면이 나온다

widget.h

Copy
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QLCDNumber>
#include <QLineEdit>
#include <QProgressBar>
#include <QTabWidget>

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = nullptr);
    ~Widget() override;

private:
    QLCDNumber *lcd;
    QLineEdit *edit;
    QProgressBar *progress;
    // 최대~최소를 지정해서 값을 세팅해서 이동
    // 일반적으로 가로
    QTabWidget *tab;
};
#endif // WIDGET_H

widget.cpp

Copy
#include "widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
		lcd = new QLCDNumber(2, this);
    lcd->setGeometry(50, 30, 200, 100);
    lcd->setSegmentStyle(QLCDNumber::Filled);
    lcd->display(10);

    edit = new QLineEdit("초기값", this);
    edit->setGeometry(50, 150, 200, 30);
    edit->setEchoMode(QLineEdit::Normal);

    progress = new QProgressBar(this);
    progress->setGeometry(50, 200, 200, 30);
    // progress->setMinimum(0);
    // progress->setMaximum(100);
    progress->setRange(0, 100); // min max 한꺼번에 지정
    progress->setOrientation(Qt::Horizontal);
    progress->setInvertedAppearance(false);
    progress->setValue(20);
}

Widget::~Widget() = default;

(디자이너(사람) 없이 배치하기 위한 방법으로… 격자 좌표에 그림 그려서 좌표값을 정하는게 있다…)

(군대에서 많이 쓴다고 한다………….)

화면 구성 이후 로직을 만들면 된다

c.f.)그리고 실습코드를 따라치면 공부가 안되니까 하나씩 같이 따라가는 것이다

c.f.) 한 번에 다 만들어놓고 실행해보면 오류가 엄청 나오고 어디서 나오는지도 찾기 어려우니까, 하나씩 만들고 실행해보고 하는 것이 좋다

어 근데 new로 만들었는데 delete 안했네

(아니근데실수거나 이따 쓰시겠지해서 질문 안했는데 의문 안가졌다고 잔소리들음)

사실은 짜잔~ this를 썼으니가 해결됐읍니다

윗놈이 사라지면 얘도 사라짐 (확인 한번 다시 찾아봐야겟다)

탭은 여러 계층을 가질 수 있게 하는 컨테이너.

어떤 위젯이 들어갈지보다, 서브를 몇 개를 만들 것이냐를 먼저 결정해야 함 (이번 실습에서는 3개 탭)

Copy
    tab = new QTabWidget(this);
    tab->setGeometry(20, 20, 300, 250);
    
    // 탭이 3개인 상황으로 실습
    QWidget *first = new QWidget;
    QWidget *second = new QWidget;
    QWidget *third = new QWidget;
    tab->addTab(first, "첫 번째 탭");
    tab->addTab(second, "두 번째 탭");
    tab->addTab(third, "세 번째 탭");

기본적으로는 부모를 지정해주는것이 안전하다

  • new QWidget(tab); 탭이 사라지면 사라지겟습니다
  • new QWidget(this); 부모 위젯이 사라지면 사라지겠습니다
  • new QWidget; 부모가 없습니다 (수동으로 삭제해주어야 함. 근데 tab에 add하는 순간 관계가 설정됨 = 컨테이너가 사라질 때 같이사라짐)

아까 만들었던 위젯들의 부모들을 this (메인 위젯)에서 first, second, third로 바꾼다

Copy
    lcd = new QLCDNumber(2, first);
    edit = new QLineEdit("초기값", second);
    progress = new QProgressBar(third);

(한 화면에 띄우기 위해 좌표를 안겹치게 배치하였으므로 그 위치 그대로 잘 나온다)

시그널 중 문자열이 바뀌었을 때 동작하게

왜 SIGNAL() SLOT()을 쓰는가? (올드스타일)

뉴스타일은 오버로딩 구분이 안됨 (&QWidget::textchanged 등… 검색해봐야댐)

올드스타일은 오버로딩된 시그널이 여러 개 있을 수 있기 때문에 구분해서 쓸 수 있음

단순하면 뉴 스타일도 괜찮지만, 만들다보면 오버로딩 많이 생기니까 올드스타일을 추천함

시그널 기억 못한다? 괜찮음

Ctrl + Enter 누르면 힌트 나옴

이 중 리턴프레스 잘봐야함

보통 입력 다 하면 엔터키 치잖아. 그래서 자주 씀

(일단 지금은 textChanged(QString) 시그널로 쓸거임)

실행했는데 connect 함수 있는 부분에서 문제가 생겼다

혹시몰라서 cmakelist에 오토컴파일도 넣었는데 안됐다

Copy
    lcd = new QLCDNumber(2, first);
    lcd->setGeometry(50, 30, 200, 100);
    lcd->setSegmentStyle(QLCDNumber::Filled);
    lcd->display(10);
    connect(edit, SIGNAL(textChanged(QString)),
            this, SLOT(textChanged(QString)));

    edit = new QLineEdit("초기값", second);
    edit->setGeometry(50, 150, 200, 30);
    edit->setEchoMode(QLineEdit::Normal);

진짜 설마설마했는데 edit이 선언되는 줄보다 위에서 edit을 connect함수로 써서 그랬다….

edit 아래로 내리니까 동작한다….

아니그러면Issues에라도띄우던가 문법오류없다매샤갈

Copy
    lcd = new QLCDNumber(2, first);
    lcd->setGeometry(50, 30, 200, 100);
    lcd->setSegmentStyle(QLCDNumber::Filled);
    lcd->display(10);

    edit = new QLineEdit("초기값", second);
    edit->setGeometry(50, 150, 200, 30);
    edit->setEchoMode(QLineEdit::Normal);
    connect(edit, SIGNAL(textChanged(QString)),
            this, SLOT(textChanged(QString)));

한글 입력해보면 다음 글자 넣을때 디버그에 바로 안들어온다

(당연함 받침 들어올 수 있으니까 기다려야함)

지금은 함수명을 textChanged로 만들었는데, 원래 함수랑 이름을 꼭 맞출 필요는 없다

(오히려 다르게 하라는 사람도 있다)

가장 많이 쓰이는건: 콤보박스 푸시버튼 라인에디트 리스트 등

단순히 보여주기 위해 (특히 힌트를 주기 위해) 레이블이 많이 쓰임 (ID [입력하세요] 이거 말하시는듯)

라인에디트에 함수 걸어서 특정 규칙에 맞게 입력하도록 할 수도 있음

프로그래스 바도 꽤 쓰이는데, 이건 멀티쓰레드 or 타이머랑 연동해서 써야함

(작업이 어느정도 되면 프로그래스 바에 신호 줘서 바꾸게 함)

탭위젯도 많이 쓴다. 옵션 등 (탭 별로 여러 개를 선택할 수 있게)

QtDesigner 이용해서 만들기

이번엔 탭에 Date/Time Edit을 넣었다

이 중 displayformat속성을 통해 날짜/시간 포맷을 지정할 수 있다

역시나, 진짜로 작동하는 시계를 만드려면 스레드로 지금 시간을 갱신해서 출력해야한다

라인에딧: 텍스트 이외에도 넣을 수 있음

텍스트에딧: 텍스트만 넣을 수 있음

lineWrapMode: 길이가 길면 줄을 어떻게 처리할지

(아래에서는 widgetwidth로 설정해서, width보다 길어졌을 때 공백 위치에서 끊어서 다음줄로 넘겻다)

placeholder: 힌트를 내부에서 흐리게 보여주는거

또한 로직 설정도 go to slot으로 간단하게 연결해둘 수 있다

(프로젝트 만들 때 .ui 사용한다고 했으므로 cmakelist에 오토컴파일 모드 on 구문도 생성되엇다)

두번째 탭에 스핀박스를 추가하고, 스핀박스 slot 중 valuechanged를 선택해서 lcd값을 바꿀 수도 잇다

Copy
void Widget::on_spinBox_valueChanged(int arg1)
{
    ui->lcdNumber->display(arg1);
}

레이아웃

그래픽 좌표계는 음수 없고, -y축을 사용한다 (모두 알고있다)

좌표계는 절대/상대가 있다

절대좌표계는 전체 창을 기준으로 위치시킴

상대좌표계는 내가 속한 곳을 전체로 보고 0,0부터 시작하는것

setGeometry( 시작점 location , 크기 size)로, (x, y, w, h) 세팅했엇다

문제는, 창을 늘이거나 줄여도 위젯의 위치나 크기가 변하지 않아서 어색해진다

그러면 값 대신 비율을 사용해서 배치하자! → 레이아웃

창의 크기가 변경될 때마다 GUI가 동적으로 변한다

(c.f. 창 크기를 못바꾸게 해서 해결하는 방법도 있긴 하다)

horizontal / vertical / grid로 만들었다

엥 안되네

(뭐 세팅 해야되니까 일단 패스)

전체 창(이 프로젝트에서는 widget)에 우클릭 > Lay out > Lay Out in a Grid 등으로 지정해서 넣으면 된다

row/column stretch 값,값,… 으로 비율 지정할 수 있다

columnstretch를 2,1로 지정해서, 왼쪽이 더 길어졌다

(근데 버튼 최소 사이즈가 있으니까 끝없이 줄일 수는 없음)

사실 디자이너에서는 예쁘게 배치하기 어렵다

여러 개를 중첩하고 어떻게 하고…

그래서 레이아웃은 코드로 쓰는게 편함 (사실 이거도 숫자 계산 잘못하면 이상해져서 어려움)

창 고정시켜버리면 가장 편하다

QGridLayout *defaultLayout = new QGridLayout();

setLayout(defaultLayout); 으로 기본 레이아웃을 설정해줘야 창 크기를 바꾸었을 때 내부에 적용된다

(보통 그리드레이아웃을 기본으로 많이 사용한다)

(레이아웃 예시 코드를 실행시킨 화면)

Qt의 데이터 타입

QString 등이 존재하고, QString↔char* , CString↔char* 변환이 가능하다

만약 네트워크를 통해 Qt에서 문자열을 QString으로 보냈는데, MS Visual C++에서 CString으로 받으면?

당연히 못알아먹는다

따라서 보내기 전 QString→QByteArray 으로 변환해서 보내는 식으로 해결할 수 있다

기본 데이터 타입

(교재 107-108페이지 / pdf기준 113-114페이지 참고)

bool도 true / false로 2가지이지만 최소 8bit로 할당해두고, quint32 등 signed와 비트수로 구분할 수 있다

(당연히 이걸 안쓰고 그냥 int도 작동한다)

문자열: const char* , const 붙은 것을 기억하자

기본 수학 함수도 지원한다 abs round min/max bound fuzzycompare 등등…

(중요) QByteArray 바이트 단위의 배열을 지원하기 위한 클래스. 많이쓴다

QChar 유니코드형태. 그러니까 “2Byte”임!!!!!! (실수하기 쉽다)

즉 QChar = wchar임 (QLatin1Char = char) 그래서 Qt에서는 wchar기준으로 많이 사용

QString도 유니코드 기준임 ( = wstring)

QLocale: 숫자/문자 표시 방식을 언어 표현 방식에 맞도록 변환함 (한국의 시:분:초 → 다른 나라의 초:분:시 등)

QtextStream (텍스트 기반) / QDataStream (Binary 기반)

이전에 배웠듯, 바이너리 기반이 안전함

Copy
QByteArray x("Q");
x.prepend("I love "); // 앞에 붙여라. I love Q
x.append("t -^^*"); // 뒤에 붙여라. I love Qt -^^*
x.replace(10, 1, "*"); // 바꿔라. I love Qt *^^*
Copy
QLabel *lbl = new QLabel("", this);
QString str = "Hello Qt";
QChar *data = str.data();
QString str_display;

while(!data->isNull())
{
	str_display.append(data->unicode());
	++data;
}

lbl->setText(str_display); // Hello Qt

원래 QString과 QLatin1Char는 당연히 호환이 안된다

하지만 친절한 Qt씨는 둘이 비슷하니까 연산자 오버로딩으로, 상호 연산할 수 있게 해줬다

Copy
QLatin1String latin("Qt");
QString str = QString("Qt");
if(str == latin)
 qDebug("Equal.");
else
 qDebug("Not equal.");

// 요즘 버전에서는 opterator키워드로 연산자 오버로딩을 지원하지 않음
//bool is_equal = latin.operator ==(str);
boo is_equal = latin ==(str);
if(is_equal)
 qDebug("Equal.");
else
 qDebug("Not equal.");

QLocale (대분류, 소분류)

Copy
QLocale egyptian(QLocale::Arabic, QLocale::Egypt);

QString

Copy
QString str = "Hello";

// 근데 아래 선언은 안되던데 명시적으로 바꾸라는데... 각각 QChar 안에 넣으니까 동작함
// 아니면 (char는 범위 넘어가니까) wchar_t 타입으로 선언하든...
// 옆자리분 알려주심: 정해진 타입이 적혀있음. 그래서 short형도(!) 가능함
static const QChar data[4] = {0x0055, 0x006e, 0x10e3, 0x03a3 };
QString str(data, 4);

str[1] = QChar('n');
str[2] = QChar(0x10e3); // 유니코드 일련번호로도 가능

if ( str == "auto" || str == "extern" ) {} // 비교연산자 가능

QChar 헤더로 들어가면 암시적 형변환이 가능한 경우와 명시적 형변환이 필요한 경우가 적혀있다…

(sizeof(wchar_t) > 2)로 검사하는 부분이 있는데, Windows와 Linux에서의 크기가 다르기 때문이다…….

Copy
// str.indexof("문자열", idx)를 통해 문자열의 위치를 찾을 수 있다
QString str = "We must be <b>bold</b>, very <b>bold</b>";
int j = 0;

while ((j = str.indexOf("<b>", j)) != -1) {
 qDebug() << "Found <b> tag at index position" << j;
 ++j;
}

QStringList

<< : input stream (cin이나 qDebug()에서 쓰는 것과 같음)

Copy
QStringList strList;
strList << "Monday" << "Tuesday" << "Wednesday";
QString str;
str = strList.join(","); 
// "Monday, Tuesday, Wednesday"

QTextStream

로그파일 다룰 때 많이 씀(로그파일은 사람이 읽어야 하니까 텍스트 형태로 작업함)

Copy
QFile file("in.txt");
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
 return;
QTextStream in(&file);
while (!in.atEnd()) {
 QString line = in.readLine();
 ...
}

데이터클래스

QBitArray: (일반프로그래밍에서는 잘 안씀) Bit단위 관리. setBit(idx, bool), 비트 연산 가능

QMargins: 상하좌우 얼마나 띄울것인지 (설명으로는 left top right bottom)

QPoint: x, y, z값을 처리

QRect: 사각형

QSize: 넓이와 높이

QVariant: void포인터 느낌. 만능(범용) 데이터 타입

3D, 4D는 높은 수준의 그래픽이라… QVector2D정도를 일반적으로 사용할 것

Q) 영상은 3D 아님?

A) 아님. 2D 이미지들의 모임임

 

대부분 QRect로 표현. 실수 좌표가 필요한 경우 QRectF 사용

QRegExp: 레귤러 익스프레션. 즉, 정규식 (활용도 높으니까 따로 배워야 함)

Copy
QRegExp rx("^\\d\\d?$"); // 0~99 의 정수값인 경우
rx.indexIn("123"); // return -1 
rx.indexIn("-6"); // return -1 
rx.indexIn("6"); // return 0

QRegion: 사각형과 달리 … 머시기… 드르렁…

QSize

QVariant

실습: QString 여러가지 방법으로 값 넣기

Copy
    // Qstring = const char[3]
    QString qstr1 = "q1";

    // Qstring(const Qchar)
    QChar c('q');
    QString qstr2(c);

    // Qstring(const char*)
    const char* ch = "q3";
    QString qstr3(ch);

    // Qstring(QByteArray)
    QByteArray a("Q");
    a.append("4");
    QString qstr4(a);

    // Qstring(Qstring*)
    QString qstr5(qstr4);

    // Qstring(const QChar* unicode, len)
    static const QChar unicd[2] = {QChar(0x0051), QChar(0x0036)};
    QString qstr6(unicd, 2);

    qDebug() << qstr1 << Qt::endl
             << qstr2 << Qt::endl
             << qstr3 << Qt::endl
             << qstr4 << Qt::endl
             << qstr5 << Qt::endl
             << qstr6 << Qt::endl;

QString을 std::string으로 사용하기 위해 형변환 함수를 사용할 수 있다

Copy
QString str("안녕");
std::string stdStr = str.toStdString();
const char* ptr = stdStr.c_str();

컨테이너 클래스

C++에서 제공하는 컨테이너와 유사함

QHash<Key, T>: 정렬 안됨 (대신 속도가 빠르고 좋음)

QMap<Key, T>: 정렬됨

따라서 정렬 필요성에 따라서 둘 중 선택하면 됨 (사용법은 거의 같음)

Qpair….쓸바에 해시/맵 씀 ㅅㄱ;

QList, QStack 등등…

(QVector는 Qt6에서 QList로 통합되었음!)

QSet<T>, QMap<Key, T> : 중복 불가능 T / Key

멀티셋멀티맵 : T/Key 중복 가능

QHash : 해시 테이블 기반. 해시 함수로 빠르게 찾을 수 있음. 정렬되지 않음

Copy
QHash<QString, int> hash;
hash["one"] = 1;
hash["three"] = 3;
hash["seven"] = 7;

hash.insert("twelve", 12);
int num1 = hash["thirteen"];
int num2 = hash.value("thirteen");

QMap : (설명을 키가 정렬된다고하셨는데 값 정렬 아님..>?)

해시랑 사용법 같음

QPair<T1, T2>: .first .second로 접근 가능

샤발 귀찮다 그냥 c++이랑 비슷함 ㅅㄱ