한국어
Qt
 

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

makersweb 2023.01.08 16:02 조회 수 : 3477

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 91470
180 Visual Studio Code용 Qt 확장팩 file makersweb 2024.10.09 915
179 Qt 응용프로그램에 Web 구성 요소를 표시 with Servo file makersweb 2024.04.27 1284
178 Qt Creator 에서 GitHub Copilot 사용하기 file makersweb 2024.04.13 1681
177 QtQuick 애플리케이션에 Rive 애니메이션 통합 makersweb 2024.03.31 1556
176 클라우드용 Qt file makersweb 2024.01.16 1830
175 QRhi 에 대해서 file makersweb 2023.12.29 1634
174 Android 애플리케이션 서명 구성 file makersweb 2023.12.17 1715
173 QML의 사용자 정의 Image makersweb 2023.09.17 1805
172 Base64로 인코딩된 파일을 복원 makersweb 2023.08.06 1551
171 QML에서 D-Bus 통신 file makersweb 2023.03.15 1887
170 Qt 하이브리드 애플리케이션(Hybrid App) 개발 file makersweb 2023.02.08 2214
169 Widgets(C++) 기반의 기본 스타일을 Dark 테마 및 Material 디자인 스타일로 바꾸기 file makersweb 2023.01.28 2723
168 OpacityMask 예제 file makersweb 2023.01.26 1823
167 QProcess 예제 (프로그램 재시작) file makersweb 2023.01.25 2086
166 하드디스크 드라이브 여유 공간 계산 file makersweb 2023.01.15 1577
» Qt Property(속성) 시스템 makersweb 2023.01.08 3477
164 VTK 를 사용해서 강력한 시각화(3D, Plotting, Chart)Qt 응용프로그램 개발하기 file makersweb 2022.10.16 2910
163 QProcess 보안 권고 리뷰 file makersweb 2022.09.18 1292
162 QMake 프로젝트를 CMake 프로젝트로 변환 with qmake2cmake makersweb 2022.09.17 1880
161 Qt Safe Renderer 개요 file makersweb 2022.09.08 1256