2012년 6월 16일 토요일

[QML/Qt] Lecture 03. QML에서 Qt Class의 Method를 호출하자.

Lecture 02.에서 만든 환경을 그대로 이용해서, QML에서 Qt Class의 메소드를 호출하는 방법을 알아봅니다.

Q_PROPERTY로 등록한 프로퍼티는 변수처럼, 읽고, 쓸 수 있는 기능을 하는 것으로 생각을 하면, 클래스의 특정 함수를 QML에서 호출하는 것은 Q_INVOKABLE을 이용할 수 있습니다.


class MyObject : public QObject
{
  Q_OBJECT
public:
  explicit MyObject(QString n, QObject *parent = 0);
  Q_PROPERTY (QString myName READ getName WRITE setName NOTIFY nameChanged)
  QString getName(void) { return name; }
  void    setName(QString n) { name = n; nameChanged();}
  Q_INVOKABLE QString getMyNameByMethod(QString param, int value)
  {
      qDebug() << __FUNCTION__ << " param:" << param << ", value:" << value;
      return name;
  }
 
signals:
  void nameChanged();
 
public slots:
private:
  QString name;
 
};


위에 녹색으로 표시한 것이 일반 Public 함수를 QML에서 보이도록 Q_INVOKABLE로 만든 함수입니다.

이 함수는 Qt의 다른 클래스에서도 Public 함수로 호출을 할 수 있으며, 이 클래스가 QDeclarativeView의 rootContext에 Property로 등록이 되면, 이 함수를 QML에서 호출 할 수 있습니다.
즉, Qt/QML의 Meta system에서 Q_INVOKABLE로 선언된 함수를 등록을 해 두므로, QML에서 호출 할 수 있게 되는 것입니다.
QML에서는 어떻게 호출은 하는가.
아래에 Main함수에서 MyObject를 Property인 “myObject”로 등록을 했습니다.

  DeclarativeView view;
  MyObject myObject("David Bae");
  view.rootContext()->setContextProperty("myObject", &myObject);
  view.setSource(QUrl("../QLecture/qml/QLecture/main.qml"));
  view.show();


그리고 QML에서 myObject의 getMyNameByMethod함수를 호출할 수 있게 됩니다.

   onClicked: {
       var name = myObject.getMyNameByMethod("nameFromQML", 50);
       console.log("[QML] getMyName:"+ name)
       idButtonText.text = name;
   }


위와 같이 호출을 할 수 있게 되고, 파라미터를 넘기거나, 리턴값을 받아 올 수 있습니다.

[QML/Qt] Lecture 02. Property를 등록하자.

Lecture 01에서 만든 환경에서, Class인 MyObject를 만들고, 거기에 Property 인 name을 등록을 하자. 그리고, 내용을 MyObject에서 이름을 가져와서, QML에서 표시를 해 보자.

1. QObject에서 상속받은 MyObject 클래스를 만들자.
 Menu > File > New File or Project 를 선택하고, C++인 Class를 선택하자.
 이름은 MyObject로 하고, QObject에서 상속을 받도록 만들자.
#ifndef MYOBJECT_H
#define MYOBJECT_H
#include <QObject>
class MyObject : public QObject
{
  Q_OBJECT
public:
  explicit MyObject(QObject *parent = 0);

signals:
public slots:

};
#endif // MYOBJECT_H

: 기본적으로 클래스를 만들면 위와 같이 기본 클래스가 만들어진다.

2. 만든 MyObject에 name을 추가하고,  Q_PROPERTY로 등록하자.
#ifndef MYOBJECT_H
#define MYOBJECT_H
#include <QObject>
class MyObject : public QObject
{
  Q_OBJECT
public:
  explicit MyObject(QString n, QObject *parent = 0);
  Q_PROPERTY (QString myName READ getName WRITE setName NOTIFY nameChanged)
  QString getName(void) { return name; }
  void    setName(QString n) { name = n; nameChanged();}
 
signals:
  void nameChanged();
 
public slots:
private:
  QString name;
 
};
#endif // MYOBJECT_H

: 위에 녹색으로 배경이 된 부분이 추가된 부분이 된다.
QML에서 myName이라는 프로퍼티이름으로 name을 읽을 수 있는데,
text: myObject.myName 이라고 쓰면, getName()이 호출되어서, 값을 넘겨주게 되고,
myObjectt.myName = “david” 라고 하면, setName(..)이 호출되어 값을 설정하게 된다.
설정될 때, nameChange() 시그널을 발생시켜서, QML에서 바인딩 된 다른 곳에서도 데이터가 변경이 된다. 이건 Property Binding과 Signa/Slot을 설명하고 나면 자세하게 알게 된다.
여기서는 nameChange라는 함수를 통해서, QML에서 자동으로 값을 읽어가게 된다는 것을 알고 가자.

3. Main에서 MyObject의 인스턴스를 만들고, view의 rootContext에서 이것을 등록하자.
#include <QApplication>
#include <QDeclarativeContext>
#include <QDeclarativeView>
#include "myobject.h"
Q_DECL_EXPORT int main(int argc, char *argv[])
{
  QApplication app(argc, argv);
  QDeclarativeView view;
  MyObject myObject("David Bae");
  view.rootContext()->setContextProperty("myObject", myObject);
  view.setSource(QUrl("../QLecture/qml/QLecture/main.qml"));
  view.show();
  return app.exec();
}

main함수에서 “myObject”로 view의 rootContext에 등록을 했으므로, QML에서 “myObject”라는 이름을 사용하면, 다른 QML콤포넌트의 id에 접근하듯이 등록된 클래스에 접근을 할 수 있게 된다.
view의 setSource를 통해서 QML파일을 등록하기 전에, rootContext에 “myObject”라는 이름으로 myObject를 등록을 하면, setSource에서 등록되는 QML에서는 myObject라는 이름으로 사용할 수 있게 된다.


4. QML에서 등록된 MyObject를 읽어와서, 내부의 Name을 읽어서 화면에 표시를 하자.
QML에서 myName을 읽으면, MyObject의 getName이 호출되고, myName에 이름을 넣으면, setName이 되는 것이다.

main.qml
Rectangle {
  width: 360
  height: 360
  Text {
      text: qsTr("Hello World2")
      anchors.centerIn: parent
  }
  Text {
      x:50; y:50
      text:myObject.myName;
  }
}


그럼, Main.qml에서 사용자가 마우스 클릭으로 이름을 바꾸고, 바꾼 내용이 화면이 표시가 되는지 확인해 보자.

QML에 버튼 비슷한 것을 만들고, click이 되면, 이름을 “Jamie”로 바꿔보자.
// import QtQuick 1.0 // to target S60 5th Edition or Maemo 5
import QtQuick 1.1
Rectangle {
  width: 360
  height: 360
  Text {
      text: qsTr("Hello World2")
      anchors.centerIn: parent
  }
  Text {
      x:50; y:50
      text:myObject.myName;
      color:"green"
  }
  Rectangle{
      x:150;y:50
      width:100;
      height:15
      color:"steelBlue"
      Text{
          anchors.fill:parent;
          verticalAlignment: Text.AlignVCenter;
          horizontalAlignment: Text.AlignHCenter;
          text: "Change"
      }
      MouseArea{
          anchors.fill: parent;
          onClicked: {
              myObject.myName = "Jamie";
          }
      }
  }
}

실제 동작하는 화면을 보면, Change를 클릭하면, David Bae라는 이름이 Jamie로 바뀌고, 화면에 자동으로 갱신이 된다.


좀더 자세하게 Q_PROPERTY를 설정할 때 들어가는 내용을 알아봅시다.
(참조 사이트: http://doc.qt.nokia.com/4.7/qobject.html#Q_PROPERTY )
Q_PROPERTY(type name
           READ getFunction
           [WRITE setFunction]
           [RESET resetFunction]
           [NOTIFY notifySignal]
           [DESIGNABLE bool]
           [SCRIPTABLE bool]
           [STORED bool]
           [USER bool]
           [CONSTANT]
           [FINAL])

선언
- 일반적인 변수와 동일하게 동작함.
- 추가적으로 Meta-Object System을 통해서 접근이 가능한 Property가 됨.
- READ : reading the property value
- WRITE: setting the property value
- RESET: default value로 돌아올 때
- NOTIFY: 변경을 알리는 Signal을 명시
- DESIGNABLE: GUI design tool에서 보이도록
- SCRIPTABLE: script engine에서 접근 가능, 디폴트로 true임
STORED: property가 자기것으로 가지고 있거나, 혹은 다른 Property의 Value에 따라 변하는지를 표시한다. 일반적으로 True, minimumWidth()의 경우, minimumSize()의 값이 되므로 자기것을 가지고 있을 필요 없다는 것임.
- USER: 사용자가 보고, 변경할 수 있는 것인지를 표시하는 것. (??)
- CONSTANT: const이므로 READ함수가 같은 값을 return한다.
- FINAL: 상속받은 class에서 overridden을 하지 못한다는 의미.