한국어
Qt
 

General and Desktop Qt Property(속성) 시스템

makersweb 2023.01.08 16:02 조회 수 : 1743

Property(속성)란?

프로퍼티는 객체지향 프로그래밍에서 객체를 모델링하고 추상화 할 때 정의한다. 예를 들어 QWidget이라면 그 폭( width )이나 높이( height ), QLabel이면 그 문자열( text )등의 프로퍼티가 있다.

일반적으로 C++ 에서는 이러한 프로퍼티의 값은 getter 라고 하는 함수를 통해 얻고 setter 라고 하는 함수로 설정한다. 예를 들어 QLabel의 경우 QLabel::text() 가 getter 이고 QLabel::setText() 가 setter 가 된다. 이와 같이 C++ 에서는 클래스 메서드를 통해서 프로퍼티를 다루는 반면 QML 로 액세스 하는 경우에는 getter / setter 가 QML엔진측에 노출되고 자동적으로 사용되어 QML에서 속성은 변수처럼 사용한다.

Qt는 Meta-Object 시스템과 유기적으로 작동하는 프로퍼티를 선언하기 위해 특별한 매크로를 제공한다.

Q_PROPERTY 매크로

C++ 클래스에서 속성을 만들려면 Q_PROPERTY 매크로를 사용하면 된다. 최소한 필요한 것은 프로퍼티의 type 과 getter 이다. 읽기와 쓰기를 모두 허용하려면 setter 도 필요하다. QML로 바인딩하는 경우 값이 변경되었음을 알리는 SIGNAL도 선언되어야 한다. 기본 서식은 다음과 같다. 이 기본 형식은 Qt 5와 Qt 6에서 모두 공통이다.

Q_PROPERTY(type name READ name WRITE setname NOTIFY nameChanged)

Qt 5 에서 사용 할 수 있는 Q_PROPERTY 의 전체 옵션은 다음과 같다.

Q_PROPERTY(type name
           (READ getFunction [WRITE setFunction] |
            MEMBER memberName [(READ getFunction | WRITE setFunction)])
           [RESET resetFunction]
           [NOTIFY notifySignal]
           [REVISION int]
           [DESIGNABLE bool]
           [SCRIPTABLE bool]
           [STORED bool]
           [USER bool]
           [CONSTANT]
           [FINAL]
           [REQUIRED])

다음은 Qt 6 에서 유효한 Q_PROPERTY 매크로다. BINDABLE 이 추가된 점을 빼면 Qt 5 와 큰 차이는 없다.

Q_PROPERTY(type name
           (READ getFunction [WRITE setFunction] |
            MEMBER memberName [(READ getFunction | WRITE setFunction)])
           [RESET resetFunction]
           [NOTIFY notifySignal]
           [REVISION int | REVISION(int[, int])]
           [DESIGNABLE bool]
           [SCRIPTABLE bool]
           [STORED bool]
           [USER bool]
           [BINDABLE bindableProperty]
           [CONSTANT]
           [FINAL]
           [REQUIRED])

예를 들어 int 형의 count 라고 하는 프로퍼티을 가지는 Counter 클래스를 생각해보자. getter 로서 count() 메서드를 선언하고 setter 로서 setCount(), SIGNAL 에 countChanged() 를 사용하는 경우 다음 예제와 같이 작성할 것이다.

class Counter : public QObject
{
    Q_OBJECT
    Q_PROPERTY(int count READ count WRITE setCount NOTIFY countChanged)
public:
    Counter(QObject *parent = 0);
 
    int count() const
    {
        return m_count;
    }
 
    void setCount(int value)
    {
        if (m_count != value) {
            m_count = value;
            emit countChanged();
        }
    }
 
singals:
    void countChanged();
 
private:
    int m_count;
};

프로퍼티명과 getter 의 이름은 같게 하는 것이 일반적이다. 또 NOTIFY 로 지정하는 SIGNAL 은 그 밖에도 동시에 변화하는 프로퍼티가 있으면 동일한 것을 공유해도 괜찮다. 속성 type은 QVariant에서 지원하는 모든 유형이거나 사용자 정의 유형일 수 있다. 사용자 정의 유형은 해당 값을 QVariant 개체에 저장할 수 있도록 Q_DECLARE_METATYPE() 매크로를 사용하여 등록해야 한다.

이와 같이 Q_PROPERTY 매크로를 추가하고 관련된 메소드를 선언·정의해 프로퍼티로서 취급할 수 있게 된다. 이 프로퍼티 시스템은 QML 과 C++ 의 양쪽을 사용하는 애플리케이션에서 상호 작용하기 위해 거의 필수적인 편리한 기능이다. 다만, 프로퍼티의 수가 적으면 문제 없으나 수가 늘어나면 getter 나 setter, SIGNAL 의 생성이 매우 귀찮아져 온다. 내용도 고정되어 있어서 쓰기가 재미가 없는 경우가 많다. 최근 Qt Creator에서는 Q_PROPERTY 매크로에 커서를 놓고 리팩토링 기능으로 누락된 메서드와 멤버 변수를 생성할 수 있지만 어쨋든 귀찮은 일이다. 그래서 등장한 것이 MEMBER 지정자다.

Qt 5.1 에서 도입된 MEMBER 는 Q_PROPERTY 매크로의 기술시에 READ 나 WRITE 로 getter / setter 를 지정하는 대신에 대응하는 멤버 변수를 지정한다. 다음은 전의 Counter 클래스를 MEMBER 를 사용해 재작성한 것이다.

class Counter : public QObject
{
    Q_OBJECT
    Q_PROPERTY(int count MEMBER m_count NOTIFY countChanged)
public:
    Counter(QObject *parent = 0);
 
singals:
    void countChanged();
 
private:
    int m_count;
};

MEMBER 를 사용하면 getter / setter 가 불필요하게 되어 메소드를 작성할 필요가 없어진다. 상당한 처리는 moc 가 생성해 메타 오브젝트 시스템에 짜 넣어진다. 값을 변경할 때 통지를 하고 싶은 경우에는 SIGNAL 의 작성과 NOTIFY 로의 지정은 필요하지만 SIGNAL 의 emit 자체는 역시 moc 가 담당한다.

MEMBER는 READ 또는 WRITE 지정자와 함께 사용할 수도 있다. READ 와 MEMBER 를 지정하는 경우에는 getter 를 작성할 필요가 있지만 setter 는 moc 가 작성한다. 반대로 WRITE 와 MEMBER 를 지정하는 경우는 setter 를 작성할 필요가 있지만 getter 는 moc 가 작성한다. MEMBER 와 READWRITE 의 모두를 지정하는 것은 의미가 없기 때문에 지원의 대상 밖이다. setter 로 유효성 체크를 실시하고 싶은 경우등에 setter 만 작성하고 getter 는 생략하고 MEMBER 를 이용하는 것이 주된 사용법이다.

Meta-Object System 으로 읽고 쓰기

속성의 이름을 제외하고는 소유 클래스에 대해 아는 것이 없어도(getter / setter 역할을 하는 메서드가 없어도) 일반 함수 QObject::property() 및 QObject::setProperty()를 사용하여 속성을 읽고 쓸 수 있다.

QPushButton *button = new QPushButton;
button->setDown(true);

QObject *object = button;
object->setProperty("down", true);

WRITE 접근자를 통해 속성에 액세스하는 것이 더 빠르고 컴파일 시간에 더 나은 진단을 제공하기 때문에 더 낫지만 이 방법으로 속성을 설정하려면 컴파일 시간에 클래스에 대해 알아야 한다. 이름으로 속성에 액세스하면 컴파일 타임에 알지 못하는 클래스에 액세스할 수 있다. QObject, QMetaObject 및 QMetaProperties 를 쿼리하여 런타임에 클래스의 속성을 알 수 있다.

QObject *object = ...
const QMetaObject *metaobject = object->metaObject();
int count = metaobject->propertyCount();
for (int i=0; i<count; ++i) {
    QMetaProperty metaproperty = metaobject->property(i);
    const char *name = metaproperty.name();
    QVariant value = object->property(name);
    ...
}

위의 스니펫에서 QMetaObject::property()는 알 수 없는 클래스에 정의된 각 속성에 대한 메타데이터를 가져오는 데 사용된다. 속성 이름은 메타데이터에서 가져와 QObject::property()에 전달되어 현재 객체의 속성 값을 가져온다. 다시 말하지만 property() 와 setProperty() 는 퍼포먼스적으로는 적절하게 생성된 getter / setter 보다 안 좋기 때문에 C++ 측으로부터 빈번하게 액세스 하는 프로퍼티에는 적합하지 않을 수도 있다.

Dynamic Properties

QObject::setProperty()를 사용하면 런타임에 클래스의 인스턴스에 새 속성을 추가할 수도 있다. 주어진 이름의 속성이 QObject에 존재하지 않는 경우(즉, Q_PROPERTY로 선언되지 않은 경우), 주어진 이름과 값을 가진 새 속성을 자동으로 QObject에 추가한다.

MyClass myclass; // MyClass is Subclass of QObject.
myclass.setProperty("text", "Hello world");
qDebug() << myclass.property("text").toString();

물론 속성 이름과 잘못된 QVariant 값을 QObject::setProperty()에 전달하여 인스턴스에서 속성을 제거할 수도 있다(QVariant의 기본 생성자는 잘못된 QVariant를 생성한다).

myClass.setProperty("text", QVariant());
번호 제목 글쓴이 날짜 조회 수
공지 Qt프로그래밍(QtQuick) Beginner를 위한 글 읽는 순서 운영자 2019.01.05 85859
50 Qt 응용프로그램에 Web 구성 요소를 표시 with Servo file makersweb 2024.04.27 14
49 Qt 하이브리드 애플리케이션(Hybrid App) 개발 file makersweb 2023.02.08 865
48 Widgets(C++) 기반의 기본 스타일을 Dark 테마 및 Material 디자인 스타일로 바꾸기 file makersweb 2023.01.28 1136
» Qt Property(속성) 시스템 makersweb 2023.01.08 1743
46 VTK 를 사용해서 강력한 시각화(3D, Plotting, Chart)Qt 응용프로그램 개발하기 file makersweb 2022.10.16 1595
45 QProcess 보안 권고 리뷰 file makersweb 2022.09.18 597
44 clazy 로 13개의 시그널, 슬롯 오류 해결 makersweb 2022.08.23 566
43 단일 인스턴스 Qt 응용 프로그램(Single-instance Application) makersweb 2022.06.23 700
42 VirtualKeyboard 스타일 커스터 마이징 makersweb 2022.03.13 462
41 Qt 응용프로그램에서 PDF 문서 렌더링 file makersweb 2021.09.23 677
40 Qt 를 사용하거나 기반으로 하는 응용프로그램 file makersweb 2021.01.30 3793
39 UI 폼(Form)작성 시 탭 순서(Tab Order) 설정 file makersweb 2020.08.24 2771
38 Qt기반의 서버와 클라이언트간 SOAP(Simple Object Access Protocol) file makersweb 2020.05.11 971
37 콘솔에서 사용자 입력받기 file makersweb 2020.03.22 51845
36 QOpenGLWidget 을 투명하게 적용 file makersweb 2020.02.05 1031
35 2020년에 변경되는 Qt 오퍼 (Qt offering changes 2020) [2] j2doll 2020.01.31 720
34 Qt 멀티 스레드 프로그래밍 시 유의해야 할 몇 가지 makersweb 2020.01.13 4867
33 Qt의 오픈소스 라이센스 소개 file makersweb 2019.12.15 12549
32 Qt Marketplace 발표 makersweb 2019.12.02 617
31 QOpenVirtualkeyboard(Qt 5용 한글 및 영문, 숫자 가상키보드) file makersweb 2019.11.27 2240