IT_Study/Embedded System

Embedded System (6) : 객체 지향 프로그래밍, Qt & GUI Framework 예제

__Vivacé__ 2023. 5. 8. 17:34

객체 지향 프로그래밍 (Object Oriented Programming)

프로그램을 객체라는 기본 단위로 나누고, 상호작용으로 구현하는 방식

 

함수 기반 절차 지향 프로그래밍 --> 소스 코드 변경 시, 함수들이 여러 곳에 영향을 끼칠 수 있어 유지 보수가 어려움

OOP는 이런 한계점을 극복

 

 

구조체 vs 클래스

단순하게 구조체에 함수 + *접근제한자가 추가된 것

구조체와 클래스의 차이 시각화

접근 제한자 (Access Modifier)
 - 클래스 내에 멤버(필드, 메서드, 속성, 이벤트 등)에게 접근하는 것을 제한하는 역할
 - 클래스 멤버의 default는 private / 구조체 멤버의 default는 public

접근 제한자 같은 클래스에서의 접근 하위 클래스에서의 접근 모든 곳으로부터의 접근
private O X X
protected O O X
public O O O

 

OOP 구조

클래스 정의 부에는 선언만 하고, 다른 소스 파일에 필드/메서드 구현을 진행

class 정의 시, 메모리에는 할당되지 않음 // 이를 인터페이스라고 함

 

캡슐화

객체가 구현된 방식의 세부 정보를 사용자로부터 숨기면서 유지하는 방식

 - 사용자는 객체의 공개 인터페이스를 통해 접근할 수 있다
 - 클래스의 멤버변수 구현 세부정보는 private로 설정하고, 멤버함수 공개 인터페이스는 public으로 설정

 

C++ : 객체 지향 + 절차 지향 - private / public이 존재 --> 캡슐화에 유리

Java : 객체 지향 - public만 존재

 

 

getter / setter

클래스의 private 멤버변수 필드에 접근할 수 있도록 만든 멤버함수 메서드

 - private 멤버변수의 값을 바꿀 때, set + 함수이름
 - private 멤버변수의 값을 가져올 때, get + 함수이름

 

 

객체 생성 방법

using namespace std;

class Sol {
private:
    int radius = 10;
};


int main() {
    // 1. Stack에 선언
    Sol me1;

    // 2. Heap에 선언
    // 메모리 누수 방지를 위해 꼭 delete를 해줘야 함
    Sol* me2 = new Sol();
    
    delete me2;
    
    return 0;
}

 

객체 생성자 및 초기화

// 생성자 --> class 이름과 같은 함수 이름을 사용

class Circle{
private:
    double radius;
public:
    Circle();  // 기본 생성자
    Circle(double r);  // 매개변수가 있는 생성자
    Circle(const Circle& circle);  // 복사 생성자
};

------------------------------------------

// 초기화 --> 대입 연산자 활용

// 1. this 활용
Marine::Marine(int hp, int power, double speed)
{
    this->hp = hp;
    this->power = power;
    this->speed = speed;
}

// 2. 멤버 초기화 리스트 활용
//    - ":" 로 시작
//    - "," 로 초기화할 멤버 변수 구분
//    - ";" 으로 끝나지 않음
Marine::Marine(int h, int p, double s):hp(h),power(p),speed(s)
{
    // 위처럼 하면, 생성자 본문에서 멤버에 값을 할당하는 연산은 안해도 됨
}

 

소멸자

// 생성자 --> class 이름과 같은 함수 이름을 사용

class Circle{
private:
    double radius;
public:
    Circle();
    Circle(double r);
    Circle(const Circle& circle);
    ~Circle();
};

이름 앞에 ~ 사용, public이 필수 / 객체에 할당된 메모리를 해제

 

상속

일반적으로 상속은 부모가 가진 요소들을 자식들이 물려받아 사용하는 것을 의미

OOP에서 상속은, 부모 / 자식 관계보다는 "코드 중복 방지" 를 위해 공통적인 요소를 일반화시킨 것

OOP 상속 예시 / 중복방지를 위한 Robot이라는 일반화 class 생성

#include <iostream>

class Machine {
public:
	std::string ver;
};

class Robot : public Machine {
public:
	void run()
	{
		std::cout << "Hi" << std::endl;
	}
};


int main() {

	Robot* a = new Robot();

	a->run();
	a->run();
	a->ver = "1.99";
	
	return 0;
}

 


Qt GUI Framework

C++ 기반 GUI App 개발 시 널리 쓰이는 프레임워크

 - 다른 플랫폼으로 porting이 자유로움

 - Windows, Linux, MacOS, Android 등 지원

 

Qt 라이선스

 - Commercial 라이선스

     - 상용적으로 사용 가능

     - 소기업은 약 50만원 / 년

     - 그 외 기업은 약 450만원 / 년

 

 - Opensource 라이선스

     - GPL : 모든 소스코드를 공개해야 함 (비 상업용으로 사용)

      (보통 Qt Chart를 이용해서 개발한다 하면, Qt chart의 라이센스가 GPL이었으면 QT chart를 이용한 코드만 공개)

     - LGPL : 특정 모듈을 쓰지 않으면 LGPL로 (상업용으로 사용 가능)

위 표에 있는 모듈을 쓰지 않으면, LGPL 가능

 

Python으로 Qt 다루기

*Python Binding : 다른 언어로 제작된 library를 python으로 호출할 수 있는 연결 모듈을 의미

 

유명한 Python Binding Set 

 1. PySide : Qt 공식 Framework, LGPL 기본 (일부 GPL)

 2. PyQt : 비공식, reference가 많음, GPL / 사용 (60 만원 / 년)

 

 

예제

from PySide6.QtWidgets import *

def ifClicked():  # 버튼이 클릭되었을 때 실행될 함수를 정의
    alert = QMessageBox()  # 새로운 메시지 박스 인스턴스를 생성
    alert.setText(tbox.text())  # 메시지 박스에 텍스트 상자의 텍스트를 설정
    alert.exec()  # 메시지 박스를 실행

app = QApplication()  # QApplication 인스턴스를 생성
win = QWidget()  # QWidget 인스턴스를 생성

win.setWindowTitle("forPractice")  # 창 제목을 설정
win.setGeometry(0, 0, 400, 300)  # 창의 위치와 크기를 설정

tbox = QLineEdit(win)  # 텍스트 상자를 생성하고 창에 추가
btn = QPushButton("PUSH", win)  # 버튼을 생성하고 창에 추가
hlay = QHBoxLayout()  # 수평 레이아웃을 생성

hlay.addWidget(tbox)  # 텍스트 상자를 레이아웃에 추가
hlay.addWidget(btn)  # 버튼을 레이아웃에 추가
win.setLayout(hlay)  # 창의 레이아웃을 설정

btn.clicked.connect(ifClicked)  # 버튼 클릭 이벤트를 ifClicked 함수와 연결

win.show()  # 창을 활성화
app.exec()  # 애플리케이션을 실행

 

from PySide6.QtWidgets import *

def setText():  # 나이 확인 버튼 클릭 시 실행되는 함수를 정의
    targetText = line2.text()  # 나이 입력창의 텍스트를 가져옴
    if not 0 < int(targetText) < 150:  
        warn3.setText("나이가 너무 많습니다")  # 메시지를 출력
    else:
        warn3.setText("통과")
        
def validation():  # 회원가입 버튼 클릭 시 실행되는 함수를 정의
    alert = QMessageBox()  # 메시지 박스 인스턴스를 생성
    targetText = line1.text()  # 이름 입력창의 텍스트를 가져옴

    if 1 <= len(targetText) <= 4:
        alert.setText("회원가입 성공!")  # 메시지를 출력
    else:
        alert.setText("이름은 1~4글자 필수")

    alert.exec()  # 메시지 박스를 실행합니다.

app = QApplication()  # QApplication 인스턴스를 생성
win = QWidget()  # QWidget 인스턴스를 생성

form = QFormLayout()  # QFormLayout 인스턴스를 생성합

line1 = QLineEdit()  # 이름 입력창을 생성
form.addRow("name", line1)  # 이름 입력창을 레이아웃에 추가

line2 = QLineEdit()  # 나이 입력창을 생성
btn2 = QPushButton("나이확인")  # 나이 확인 버튼을 생성
btn2.clicked.connect(setText)  # 버튼 클릭 시 setText 함수가 실행되도록 연결

hlay2 = QHBoxLayout()  # 수평 레이아웃을 생성
hlay2.addWidget(line2)  # 나이 입력창을 레이아웃에 추가
hlay2.addWidget(btn2)  # 나이 확인 버튼을 레이아웃에 추가
form.addRow("age", hlay2)  # 나이 확인 레이아웃을 폼 레이아웃에 추가

warn3 = QLabel("")  # 경고 레이블을 생성
form.addWidget(warn3)  # 경고 레이블을 폼 레이아웃에 추가

btn3 = QPushButton("회원가입")  # 회원가입 버튼을 생성
form.addRow(btn3)  # 회원가입 버튼을 폼 레이아웃에 추가
btn3.clicked.connect(validation)  # 버튼 클릭 시 validation 함수가 실행되도록 연결

`