Getting Started Programming with QML/ko

From Qt Wiki
Jump to: navigation, search
This article may require cleanup to meet the Qt Wiki's quality standards. Reason: Auto-imported from ExpressionEngine.
Please improve this article if you can. Remove the {{cleanup}} tag and add this page to Updated pages list after it's clean.

QML 프로그래밍 시작하기

사용자 인터페이스 언어 QML에 오신 것을 환영합니다. 이 문서에서는 QML을 사용하여 텍스트 편집기를 만드는 과정을 설명할 것입니다. 이 문서를 읽은 다음에는 QML과 Qt C+를 사용하여 프로그램을 개발할 수 있을 것입니다.

QML로 사용자 인터페이스 만들기

지금부터 만들 텍스트 편집기는 텍스트를 읽고, 저장하고, 텍스트 작업을 할 수 있는 편집기입니다. 이 문서는 크게 두 부분으로 나뉩니다. 첫 부분은 프로그램 레이아웃과 행동을 QML로 작성하는 것입니다. 두 번째 부분은 파일을 불러오고 저장하는 과정을 Qt C로 구현하는 것을 다룹니다. Qt의 메타 개체 시스템 사용하여 C+ 함수를 QML에서 사용할 수 있는 속성으로 내보낼 수 있습니다. QML과 Qt C+를 사용하면 인터페이스 로직과 프로그램 로직을 나눌 수 있습니다.

http://doc.qt.nokia.com/4.7/images/qml-texteditor5_editmenu.png

QML 예제 코드를 실행하려면 Qt의 일부인 qmlviewer QML 파일을 인자로 지정하여 실행사십시오. 이 문서의 C+ 부분은 독자가 Qt 컴파일에 대한 기본적인 지식을 알고 있다고 가정할 것입니다.

문서 구성:

  1. 단추와 메뉴 정의하기
  2. 메뉴 표시줄 만들기
  3. 텍스트 편집기 만들기
  4. 텍스트 편집기 꾸미기
  5. Qt C+로 QML 확장하기

단추와 메뉴 정의하기

기본 구성 요소 - 단추

텍스트 편집기를 만들기 위해서 단추부터 시작할 것입니다. 기능 상으로 단추는 마우스에 반응하는 영역과 레이블로 구성됩니다. 사용자가 단추를 누르면 동작을 실행합니다.

QML의 기본적인 시각 구성 요소는 Rectangle 원소입니다. Rectangle 원소는 모양과 위치를 결정하는 속성을 가지고 있습니다.

맨 처음 나타나는 import Qt 4.7은 qmlviewer 도구에서 나중에 사용할 QML 구성 요소를 불러오는 데 사용합니다. 모든 QML 파일에는 이 줄이 포함되어 있어야 합니다. import 문에 사용할 Qt 버전이 포함되어 있음을 주목해 주십시오.

간단한 사각형은 고유 식별자로 simplebutton을 가지며, id 속성과 연결되어 있습니다. Rectangle의 속성은 속성:값 형식으로 정의할 수 있습니다. 아래 코드 예제에서는 Rectangle의 color 속성에 grey라는 값을 지정하였습니다. 비슷하게 Rectangle의 width, height 속성에 값을 지정하였습니다.

Text 구성 요소는 편집할 수 없는 텍스트 필드입니다. 이 Text 구성 요소의 이름을 buttonLabel이라고 할 것입니다. 텍스트 필드의 텍스트를 지정하기 위하여 text 속성에 값을 지정할 것입니다. 레이블은 Rectangle 안에 포함되어 있으며, 가운데에 배치하기 위하여 Text 구성 요소의 anchor 속성을 부모인 simplebutton으로 지정할 것입니다. 다른 항목의 anchor 속성과 연결시킬 수도 있어서, 레이아웃을 간단하게 할 수 있습니다.

이 코드를 SimpleButton.qml이라는 파일에 저장할 것입니다. qmlviewer에 이 파일을 인자로 지정하면 회색 사각형과 텍스트 레이블이 표시됩니다.

http://doc.qt.nokia.com/4.7/images/qml-texteditor1_simplebutton.png

단추를 눌렀을 때 동작을 구현하기 위해서 QML의 이벤트 처리를 사용할 수 있습니다. QML의 이벤트 처리는 Qt의 시그널과 슬롯과 유사합니다. 시그널이 나오면 연결된 슬롯이 호출됩니다.

Rectangle{
 id:simplebutton
 

 MouseArea{
 id: buttonMouseArea

 anchors.fill: parent //anchor all sides of the mouse area to the rectangle's anchors
 //onClicked handles valid mouse button clicks
 onClicked: console.log(buttonLabel.text'' " clicked" )
 }
 }

방금 만든 simplebutton에 MouseArea 구성 요소를 추가할 것입니다. MouseArea 구성 요소는 마우스 이동을 감지할 영역을 지정합니다. 이 버튼의 MouseArea simplebutton 전체를 지정할 것입니다. anchors.fill은 anchors라는 그룹 안에 있는 fill 속성을 가리킵니다. QML은 고정점 기반 레이아웃 사용하며, 항목끼리 서로 고정점을 지정할 수 있습니다.

MouseArea에는 MouseArea 경계 내부의 마우스 이동을 감지하는 여러 시그널 핸들러가 있습니다. 이 중 하나는 감지할 수 있는 마우스 단추를 눌렀을 때 발생하는 onClicked가 있으며, 기본적으로 왼쪽 단추 클릭을 감시합니다. onClicked 핸들러에 원하는 동작을 지정할 수 있습니다. 이 예제에서는 마우스가 눌렸을 때 console.log() 함수를 사용하여 텍스트를 출력합니다. console.log() 함수는 디버깅 및 텍스트 출력에 사용할 수 있습니다.

SimpleButton.qml 파일에 있는 코드 만으로 화면에 단추를 표시하고, 눌렸을 때 텍스트를 출력할 수 있습니다.

Rectangle {
 id:Button
 

property color buttonColor: "lightblue"
 property color onHoverColor: "gold"
 property color borderColor: "white"

signal buttonClick()
 onButtonClick: {
 console.log(buttonLabel.text + " clicked" )
 }

MouseArea{
 onClicked: buttonClick()
 hoverEnabled: true
 onEntered: parent.border.color = onHoverColor
 onExited: parent.border.color = borderColor
 }

//determines the color of the button by using the conditional operator
 color: buttonMouseArea.pressed ? Qt.darker(buttonColor, 1.5) : buttonColor
 }

Button.qml 파일에는 완전히 작동하는 단추가 정의되어 있습니다. 이 글에 등장하는 코드는 일부가 생략되어 있으며, 생략된 부분은 (…)로 표시되어 있습니다. 이전 절에서 다루었거나 문맥과는 관련 없는 코드입니다.

사용자 정의 속성은 property type name 문법으로 정의됩니다. 코드에서 color 타입 속성 buttonColor가 정의되었으며, 값으로는 "lightblue"가 지정되어 있습니다. buttonColor는 단추의 채움 색을 나타낼 때 사용할 속성입니다. 속성에 값을 지정할 때에는 :(쌍점) 이외에도 =(등호)를 사용할 수 있습니다. 사용자 정의 속성을 사용하면 Rectangle의 범위 밖에서 내부 개체에 접근할 수 있습니다. 기본 QML 자료형 int, string, real, variant가 있습니다.

onEntered와 onExited 시그널 핸들러에 색상을 바꾸는 코드를 추가하였으므로, 마우스가 단추 위를 지날 때에는 단추의 경계선이 노란색으로 표시되며, 마우스가 단추를 벗어나면 원래 색상으로 돌아옵니다.

Button.qml에는 buttonClick() 시그널이 정의되어 있으며, 시그널 이름 앞에 signal 키워드를 추가하였습니다. 모든 시그널의 핸들러는 자동으로 생성되며, 시그널의 이름 앞에 on이 추가된 형태입니다. 따라서 buttonClick의 핸들러는 onButtonClick이 됩니다. onButtonClick 핸들러는 수행할 동작과 연결되어 있습니다. 이 단추 예제에서는 onClicked 마우스 핸들러가 onButtonClick을 호출하며, 이는 텍스트를 표시합니다. onButtonClick 핸들러는 외부 개체에서 단추의 마우스 영역에 접근할 수 있도록 합니다. 예를 들어 항목에는 하나 이상의 MouseArea를 정의할 수 있으며, buttonClick 시그널은 여러 MouseArea에서 온 신호를 처리할 수 있습니다.

이제 QML의 마우스 이동 처리에 대한 기본적인 것을 알았습니다. Rectangle 안에 Text 레이블을 만들었고, 속성을 수정하였고, 마우스 움직임에 반응하도록 만들었습니다. 원소 안에 원소를 만드는 방법은 텍스트 편집기 프로그램에서 계속 사용됩니다.

단추는 동작을 수행할 때 제 기능을 발휘합니다. 다음 절에서는 여러 단추를 포함하는 메뉴를 만들 것입니다.

http://doc.qt.nokia.com/4.7/images/qml-texteditor1_button.png

메뉴 페이지 만들기

지금까지 QML 파일에 원소를 만들고 행동을 지정하는 방법에 대해서 알아보았습니다. 이 절에서는 QML 구성 요소를 불러오고 구성 요소를 재사용하는 방법에 대해서 알아볼 것입니다.

메뉴는 목록의 내용을 표시하며, 각각 항목은 동작을 수행할 수 있습니다. QML에서는 여러 방법으로 메뉴를 만들 수 있습니다. 첫째로, 서로 다른 동작을 수행하는 단추를 포함하는 메뉴를 만들 것입니다. 메뉴 코드는 FileMenu.qml에 포함되어 있습니다.

import Qt 4.7  Qt QML 모듈을 가져옴
 import "folderName" 폴더의 내용을 가져옴
 import "script.js" as Script 자바스크립트 파일을 가져오고 Script라는 이름으로 부름

위의 문법은 import 키워드를 사용하는 방법을 소개합니다. 자바스크립트 파일이나 같은 디렉터리 안에 있지 않은 QML 파일을 불러올 때 사용합니다. Buttom.qml 파일은 FileMenu.qml 파일과 같은 디렉터리 안에 있으므로, Button.qml 파일을 사용하기 위해서 가져올 필요는 없습니다. Rectangle{} 선언처럼 Button{} 선언으로 바로 Button 구성 요소를 만들 수 있습니다.

 FileMenu.qml 일부:

Row{
 anchors.centerIn: parent
 spacing: parent.width/6

Button{
 id: loadButton
 buttonColor: "lightgrey"
 label: "Load"
 }
 Button{
 buttonColor: "grey"
 id: saveButton
 label: "Save"
 }
 Button{
 id: exitButton
 label: "Exit"
 buttonColor: "darkgrey"

onButtonClick: Qt.quit()
 }
 }

FileMenu.qml 파일에서 세 개의 Button 구성 요소를 정의할 것입니다. 이들은 Row 구성 요소에 포함되어 있으며, 자식 원소를 수평으로 배치하는 데 사용됩니다. Button 선언은 Button.qml 파일에 포함되어 있으며, 이는 이전 절에서 다루었던 Button.qml 파일 그대로입니다. 단추를 새로 만들 때마다 속성을 새로 정의할 수 있으며, 이는 Button.qml 파일의 설정 대신 사용됩니다. exitButton이 눌리면 프로그램을 종료하고 창을 닫습니다. exitButton의 onButtonClick 핸들러와 더불러 Button.qml 파일에 있는 시그널 핸들러 onButtonClick도 같이 호출됩니다.

http://doc.qt.nokia.com/4.7/images/qml-texteditor1_filemenu.png

Row 선언은 Rectangle 안에 정의되어 있으며, 단추가 수평으로 배치되어 있는 직사각형 컨테이너를 만듭니다. 이 간접적인 직사각형을 통해서 메뉴 안에 단추를 배열합니다.

편집 메뉴의 선언 역시 비슷합니다. 편집 메뉴에는 잘라내기, 붙여넣기, 전체 선택이라는 이름표가 붙어 있는 단추가 포함되어 있습니다.

http://doc.qt.nokia.com/4.7/images/qml-texteditor1_editmenu.png

이전에 만든 구성 요소를 가져오고 수정하는 방법을 사용하여, 이 메뉴 페이지를 합쳐서 QML을 사용하여 메뉴 표시줄을 만들 것입니다. 메뉴 표시줄은 메뉴를 선택하는 단추를 포함할 것입니다.

메뉴 표시줄 만들기

텍스트 편집기 프로그램에서는 메뉴를 메뉴 표시줄로 표시할 것입니다. 메뉴 표시줄은 다른 메뉴간을 선택할 수 있도록 하며, 사용자는 표시할 메뉴를 선택할 수 있습니다. 메뉴 전환은 표시보다 더 많은 작업이 필요합니다. QML은 모델과 뷰를 사용하여 데이터 구조화 및 표시를 담당합니다.

데이터 모델과 뷰 사용하기

QML은 데이터 모델 표시하기 위한 데이터 뷰 가지고 있습니다. 메뉴 표시줄은 메뉴를 목록으로 표시하며, 메뉴 이름을 보여 주는 머릿글을 가지고 있습니다. 메뉴 목록은 VisualItemModel 안에 선언되어 있습니다. VisualItemModel 구성 요소는 Rectangle 구성 요소 및 가져온 UI 구성 요소에 대한 뷰를 가지고 있습니다. ListModel 같은 다른 모델은 데이터를 표시하려면 다른 방법을 사용해야 합니다.

menuListModel 안에 두 개의 항목 FileMenu/EditMenu를 정의할 것입니다. 두 개의 메뉴 구성을 편집하고 표시하는 데 ListView 사용할 것입니다. MenuBar.qml 파일에는 QML 선언이 포함되어 있으며, 간단한 편집 메뉴는 EditMenu.qml에 정의되어 있습니다.

 VisualItemModel{
 id: menuListModel
 FileMenu{
 width: menuListView.width
 height: menuBar.height
 color: fileColor
 }
 EditMenu{
 color: editColor
 width: menuListView.width
 height: menuBar.height
 }
 }

ListView 구성 요소는 지정한 방법에 따라서 모델을 표시합니다. 모델 항목을 Row 구성 요소에 배치할지, 모눈에 맞출지 등을 정할 수 있습니다. menuListModel에는 이미 보이는 항목이 있으므로, 항목을 표시할 방법을 정의할 필요는 없습니다.

 ListView{
 id: menuListView

//Anchors are set to react to window anchors
 anchors.fill:parent
 anchors.bottom: parent.bottom
 width:parent.width
 height: parent.height

//the model contains the data
 model: menuListModel

//control the movement of the menu switching
 snapMode: ListView.SnapOneItem
 orientation: ListView.Horizontal
 boundsBehavior: Flickable.StopAtBounds
 flickDeceleration: 5000
 highlightFollowsCurrentItem: true
 highlightMoveDuration:240
 highlightRangeMode: ListView.StrictlyEnforceRange
 }

추가로 ListView는 Flickable 상속받으며, 마우스 드래그나 기타 제스처에 반응할 수 있도록 합니다. 코드의 마지막 부분은 Flickable 속성을 편집하여 원하는 동작을 얻는 과정입니다. highlightMoveDuration 속성은 항목을 전환하는 시간을 설정합니다. 이 값이 크면 메뉴 전환이 더 느려집니다.

ListView는 index를 통하여 모델 항목을 관리하며, 모델의 각각 표시되는 항목은 선언된 순서대로 index로 접근할 수 있습니다. currentIndex 속성을 변경하면 ListView의 강조된 항목을 변경할 수 있습니다. 메뉴 표시줄의 헤더는 이 효과를 보여 줍니다. 단추 2개가 한 줄에 배치되어 있으며, 둘 다 눌렀을 때 현재 메뉴를 변경합니다. fileButton을 누르면 현재 메뉴를 FileMenu로 전환하며, menuListModel에서 맨 처음으로 선언되었기 때문에 index는 0입니다. 비슷하게 editButton을 누르면 현재 메뉴를 EditMenu로 전환합니다.

labelList 사각형은 z 값으로 1을 가지고 있으며, 메뉴 표시줄 위에 표시됨을 뜻합니다. z 값이 큰 개체는 z 값이 작은 개체 위에 표시되며, 기본 z 값은 0입니다.

 Rectangle{
 id: labelList
 
 z: 1
 Row{
 anchors.centerIn: parent
 spacing:40
 Button{
 label: "File"
 id: fileButton
 
 onButtonClick: menuListView.currentIndex = 0
 }
 Button{
 id: editButton
 label: "Edit"
 
 onButtonClick: menuListView.currentIndex = 1
 }
 }
 }

방금 만든 메뉴 표시줄은 메뉴 이름을 눌렀을 때 해당하는 메뉴로 전환됩니다.

http://doc.qt.nokia.com/4.7/images/qml-texteditor2_menubar.png

텍스트 편집기 만들기

TextArea 선언하기

텍스트를 편집할 수 없다면 편집기라고 부를 수 없습니다. QML의 TextEdit 구성 요소를 사용하면 여러 줄을 사용할 수 있는 편집할 수 있는 텍스트 영역을 만듭니다. TextEdit Text 다르며, Text는 사용자가 텍스트를 직접 편집할 수 없습니다.

 TextEdit{
 id: textEditor
 anchors.fill:parent
 width:parent.width; height:parent.height
 color:"midnightblue"
 focus: true

wrapMode: TextEdit.Wrap

onCursorRectangleChanged: flickArea.ensureVisible(cursorRectangle)
 }

편집기 속성으로는 글꼴 색과 텍스트 래핑 여부가 있습니다. TextEdit 영역은 프로그램 영역 내부에 있으며, 텍스트 커서가 보이는 영역 밖으로 나갈 때 텍스트를 스크롤할 것입니다. ensureVisible() 함수는 커서가 보이는 영역 밖에 있을 때 텍스트 영역을 그에 따라 움직입니다. QML은 자바스크립트를 사용하며, 앞서 이야기한 대로 자바스크립트를 QML로 가져오거나 QML 안에서 사용할 수 있습니다.

 function ensureVisible®{
 if (contentX >= r.x)
 contentX = r.x;
 else if (contentX+width <= r.x+r.width)
 contentX = r.x+r.width-width;
 if (contentY >= r.y)
 contentY = r.y;
 else if (contentY+height <= r.y+r.height)
 contentY = r.y+r.height-height;
 }

텍스트 편집기의 구성 요소 합치기

이제 QML로 텍스트 편집기를 만들 차례입니다. 텍스트 편집기에는 두 가지의 구성 요소가 있으며, 방금 만든 메뉴 표시줄과 텍스트 영역입니다. QML을 사용하면 구성 요소를 재활용할 수 있으며, 구성 요소를 가져오고 속성을 변경해서 사용할 수 있습니다. 여기서 만들 텍스트 편집기는 화면을 이등분하며, 위쪽 1/3은 메뉴 표시줄, 아래쪽 2/3는 텍스트 영역으로 사용합니다. 메뉴 표시줄은 다른 모든 구성 요소 위에 표시됩니다.

 Rectangle{

id: screen
 width: 1000; height: 1000

//the screen is partitioned into the MenuBar and TextArea. 1/3 of the screen is assigned to the MenuBar
 property int partition: height/3

MenuBar{
 id:menuBar
 height: partition
 width:parent.width
 z: 1
 }

TextArea{
 id:textArea
 anchors.bottom:parent.bottom
 y: partition
 color: "white"
 height: partition*2
 width:parent.width
 }
 }

재사용 가능한 구성 요소를 사용했기 때문에 텍스트 편집기 코드는 간단합니다. 그러면 미리 정의한 속성을 변경하지 않고 주 프로그램을 사용자 정의할 수 있습니다. 이런 접근 방법을 사용하여 프로그램 레이아웃과 UI 구성 요소를 쉽게 만들 수 있습니다.

http://doc.qt.nokia.com/4.7/images/qml-texteditor3_texteditor.png

텍스트 편집기 꾸미기

서랍장 인터페이스

방금 만든 텍스트 편집기는 매우 단순해 보이며 이제 꾸밀 필요가 있습니다. QML을 사용하면 화면 전환과 애니메이션을 구현할 수 있습니다. 메뉴 바가 화면의 1/3을 차지하고 있기 때문에 원할 때만 표시할 수 있으면 좋을 것입니다.

메뉴 표시줄을 눌렀을 때 접거나 펼 수 있는 서랍장 인터페이스를 추가할 것입니다. 이 구현에서는 마우스 클릭에 반응하는 작은 사각형을 추가할 것입니다. 서랍장과 프로그램은 "열린" 상태와 "닫힌" 상태가 있습니다. 서랍장 항목은 높이가 작은 사각형 띠입니다. 이 안에 Image 구성 요소를 추가하여 화살표 아이콘을 서랍장 가운데에 배치할 예정입니다. 서랍장은 전체 프로그램에 열린 상태를 추가하며, 식별자로는 screen을 사용합니다. 이 상태는 서랍장을 누를 때 바뀝니다.

 Rectangle{
 id:drawer
 height:15

Image{
 id: arrowIcon
 source: "images/arrow.png"
 anchors.horizontalCenter: parent.horizontalCenter
 }

MouseArea{
 id: drawerMouseArea
 anchors.fill:parent
 onClicked:{
 if (screen.state  "DRAWER_CLOSED"){
                     screen.state = "DRAWER_OPEN"
                 }
                 else if (screen.state  "DRAWER_OPEN"){
 screen.state = "DRAWER_CLOSED"
 }
 }
 
 }
 }

상태는 설정의 모음이며 State 구성 요소에 저장됩니다. 상태의 목록은 목록으로 관리할 수 있으며, states 속성 내에서만 값을 가질 수 있습니다. 이 예제에 있는 상태는 DRAWER_CLOSED와 DRAWER_OPEN입니다. 항목 속성은 PropertyChanges 속성에 정의되어 있습니다. DRAWER_OPEN 상태에서는 항목 변경에 반응하는 항목이 4개 있습니다. 첫 번째 대상 menuBar는 y 속성을 0으로 바꿉니다. 비슷하게 textArea의 위치는 DRAWER_OPEN 상태에서 바뀝니다. textArea, drawer, 서랍장의 아이콘이 현재 상태에 맞게 바뀝니다.

 states:[
 State {
 name: "DRAWER_OPEN"
 PropertyChanges { target: menuBar; y: 0}
 PropertyChanges { target: textArea; y: partition + drawer.height}
 PropertyChanges { target: drawer; y: partition}
 PropertyChanges { target: arrowIcon; rotation: 180}
 },
 State {
 name: "DRAWER_CLOSED"
 PropertyChanges { target: menuBar; y:-height; }
 PropertyChanges { target: textArea; y: drawer.height; height: screen.height- drawer.height }
 PropertyChanges { target: drawer; y: 0 }
 PropertyChanges { target: arrowIcon; rotation: 0 }
 }
 ]

상태 변경은 언제든지 일어날 수 있으며, 전환은 부드러워야 합니다. 상태 간의 전환은 Transition 원소로 정의되며, 항목의 transitions 속성의 값으로 지정할 수 있습니다. 이 텍스트 편집기는 DRAWER_OPEN이나 DRAWER_CLOSED 둘 중 하나로 상태가 바뀔 때마다 상태 전환이 일어나도록 해야 합니다. 상태 전환 효과는 from 상태와 to 상태 둘 다가 필요하며, 와일드카드 기호 를 사용하면 모든 상태 변경에 적용됨을 뜻합니다.

상태가 전환될 때 애니메이션을 지정할 수 있습니다. 메뉴 표시줄의 위치가 y:0에서 y:-partition으로 전환될 때 NumberAnimation 구성 요소를 사용하여 애니메이션을 추가할 수 있습니다. 대상의 애니메이션 지속 시간과 지속 곡선을 지정할 수 있습니다. 지속 곡선은 애니메이션의 속도와 상태 전환 시 행동을 지정할 수 있습니다. 여기에서 사용할 지속 곡선은 Easing:OutQuint, 애니메이션의 끝에 도달했을 때 속도를 줄입니다. QML의 애니메이션 대한 글을 참고하십시오.

 transitions: [
 Transition {
 to: "'''"
 NumberAnimation { target: textArea; properties: "y, height"; duration: 100; easing.type:Easing.OutExpo }
 NumberAnimation { target: menuBar; properties: "y"; duration: 100; easing.type: Easing.OutExpo }
 NumberAnimation { target: drawer; properties: "y"; duration: 100; easing.type: Easing.OutExpo }
 }
 ]

애니메이션 속성을 변경하는 또 다른 방법은 Behavior 구성 요소를 지정하는 방법입니다. Transition은 상태 전환에만 사용할 수 있으며, Behavior는 일반적인 속성 변경 시에도 사용할 수 있습니다. 텍스트 편집기에서 화살표는 rotation 속성이 바뀔 때마다 NumberAnimation 애니메이션이 표시됩니다.

 TextEditor.qml:

Behavior{
 NumberAnimation{property: "rotation";easing.type: Easing.OutExpo }
 }

구성 요소로 돌아가서 구성 요소의 모양을 개선해 봅시다. Button.qml 파일에 단추가 눌렸을 때 color와 scale 속성이 변경될 수 있도록 만들 수 있습니다. Color 형식은 ColorAnimation 사용하고, 숫자는 NumberAnimation 사용합니다. on propertyName 문법을 사용하면 속성 하나만을 다룰 때 편합니다.

 Button.qml:
 

color: buttonMouseArea.pressed ? Qt.darker(buttonColor, 1.5) : buttonColor
 Behavior on color { ColorAnimation{ duration: 55} }

scale: buttonMouseArea.pressed ? 1.1 : 1.00
 Behavior on scale { NumberAnimation{ duration: 55} }

추가적으로 그라데이션이나 투명도 등으로 QML 구성 요소의 모양을 바꿀 수 있습니다. Gradient 속성을 정의하면 color 속성을 덮어씁니다. 그라데이션에 사용할 색상은 GradientStop 정의할 수 있습니다. 그라데이션은 0.0과 1.0 사이의 위치에 배치할 수 있습니다.

 MenuBar.qml
 gradient: Gradient {
 GradientStop { position: 0.0; color: "#8C8F8C" }
 GradientStop { position: 0.17; color: "#6A6D6A" }
 GradientStop { position: 0.98;color: "#3F3F3F" }
 GradientStop { position: 1.0; color: "#0e1B20" }
 }

이 그라데이션은 메뉴 표시줄의 깊이를 흉내내는 데 사용합니다. 첫 색상은 0.0, 마지막 색상은 1.0에서 시작합니다.

더 나아가기

텍스트 편집기의 사용자 인터페이스를 만들어 보았습니다. 여기서 더 나아가면, 사용자 인터페이스는 더 이상 수정할 필요가 없으며, Qt와 C+로 프로그램 로직을 구현할 수 있습니다. QML은 프로토타입을 만들거나, UI 디자인과 프로그램 로직을 분리하는 데 사용할 수 있습니다.

http://doc.qt.nokia.com/4.7/images/qml-texteditor4_texteditor.png

Qt C로 QML 확장하기

텍스트 편집기 레이아웃을 다 구성하였으므로, 텍스트 편집기 기능을 C로 구현할 것입니다. QML과 C를 같이 사용하면 프로그램 로직을 Qt로 구현할 수 있습니다. C+ 프로그램에 Qt Declarative 클래스 사용하여 QML 컨텍스트를 만들 수 있으며, QML 구성 요소를 그래픽 장면으로 나타낼 수 있습니다. 또 다른 방법으로는 C++ 코드를 qmlviewer 읽을 수 있는 플러그인으로 만들 수 있습니다. 우리의 프로그램에서는 불러오기 및 저장하기 기능을 C+로 구현하고 플러그인으로 내보낼 것입니다. 이렇게 하면 실행 파일을 불러오지 않고 QML 만으로 프로그램을 만들 수 있습니다.

QML로 C+ 클래스 내보내기

Qt와 C+로 파일 불러오기와 저장을 구현할 것입니다. QML에서 C+ 클래스와 함수를 사용하려면 등록해야 합니다. 클래스는 Qt 플러그인으로 컴파일해야 하며, QML 파일에 플러그인의 위치를 알려 줘야 합니다.

이 프로그램에는 다음이 필요합니다:

  1. 디렉터리 작업을 수행할 Directory 클래스
  2. 디렉터리 안에 있는 파일 목록을 흉내내는 File 클래스 (QObject)
  3. QML 컨텍스에 클래스를 등록할 plugin 클래스
  4. 플러그인을 컴파일하는 Qt 프로젝트 파일
  5. qmlviewer에서 플러그인을 찾을 qmldir 파일

Qt 플러그인 만들기

플러그인을 만들려면 Qt 프로젝트 파일을 수정해야 합니다. 맨 처음으로 필요한 소스, 헤더, Qt 모듈을 선언해야 합니다. 모든 C++ 코드와 프로젝트 파일은 filedialog 디렉터리 아래에 있습니다.

 cppPlugins.pro:

TEMPLATE = lib
 CONFIG ''= qt plugin
 QT''= declarative

DESTDIR ''= ../plugins
 OBJECTS_DIR = tmp
 MOC_DIR = tmp

 TARGET = FileDialog

 HEADERS''= directory.h  file.h  dialogPlugin.h

SOURCES += directory.cpp  file.cpp  dialogPlugin.cpp

Qt를 컴파일할 때 declarative 모듈이 필요하며, 플러그인으로 설정하기 위해서 lib 템플릿이 필요합니다. 플러그인은 부모 디렉터리의 plugins 디렉터리 아래에 있어야 합니다.

QML에 클래스 등록하기

 dialogPlugin.h:

#include <QDeclarativeExtensionPlugin>

class DialogPlugin : public QDeclarativeExtensionPlugin
 {
 Q_OBJECT

public:
 void registerTypes(const char *uri);

};

플러그인 클래스 DialogPlugin은 QDeclarativeExtensionPlugin 플러그인의 하위 클래스입니다. 상속된 함수 registerTypes() 구현해야 합니다. 다음은 dialogPlugin.cpp 파일의 내용입니다.

 DialogPlugin.cpp:

#include "dialogPlugin.h"
 #include "directory.h"
 #include "file.h"
 #include <qdeclarative.h>

void DialogPlugin::registerTypes(const char '''uri){

 qmlRegisterType<Directory>(uri, 1, 0, "Directory");
 qmlRegisterType<File>(uri, 1, 0,"File");
 }

 Q_EXPORT_PLUGIN2(FileDialog, DialogPlugin);

registerTypes() 함수는 File과 Directory 클래스를 QML에 등록합니다. 이 함수는 템플릿에 사용할 클래스 이름, 주 및 부 버전 번호, 클래스 이름을 인자로 받습니다.

Q_EXPORT_PLUGIN2 매크로를 사용하여 플러그인을 내보냅니다. dialogPlugin.h 파일 안에도 클래스 선언 맨 위에 Q_OBJECT 매크로가 지정되어 있습니다. 프로젝트 파일을 인자로 qmake를 실행하여 필요한 메타 개체 코드를 생성해야 합니다.

C++ 클래스로 QML 속성 만들기

C+와 Qt의 메타 개체 시스템 QML 구성 요소와 속성을 만들 수 있습니다. 속성을 시그널과 슬롯으로 구현하여 Qt에서 인식 가능한 형태로 만들 수도 있습니다. 이 속성은 QML에서 사용할 수 있습니다.

텍스트 편집기는 파일을 불러오고 저장할 수 있어야 합니다. 대개 이 기능은 파일 대화 상자에 포함되어 있습니다. 디렉터리를 읽어오고 입출력 스트림을 구현하는 데 QDir, QFile, QTextStream 클래스를 사용할 수 있습니다.

 class Directory : public QObject{

 Q_OBJECT

 Q_PROPERTY(int filesCount READ filesCount CONSTANT)
 Q_PROPERTY(QString filename READ filename WRITE setFilename NOTIFY filenameChanged)
 Q_PROPERTY(QString fileContent READ fileContent WRITE setFileContent NOTIFY fileContentChanged)
 Q_PROPERTY(QDeclarativeListProperty<File> files READ files CONSTANT )

 

Directory 클래스는 Qt 메타 개체 시스템으로 파일 처리를 위한 속성을 등록합니다. Directory 클래스는 플러그인으로 내보내지며, QML의 Directory 구성 요소로 사용할 수 있습니다. Q_PROPERTY 매크로로 정의한 모든 속성은 QML에서 사용할 수 있습니다.

Q_PROPERTY Qt 메타 개체 시스템에 속성뿐만 아니라 읽기 및 쓰기 함수도 정의합니다. 예를 들어 QString 형식의 filename 속성은 filename() 함수로 읽을 수 있고, setFilename() 함수로 수정할 수 있습니다. 추가적으로, filename 속성이 바뀌었을 때마다 발생하는 filenameChanged() 시그널이 있습니다. read, write 함수는 헤더 파일에서 public으로 선언되었습니다.

비슷하게 다른 속성도 정의되어 있습니다. filesCount 속성은 디렉터리에 있는 파일 개수를 나타냅니다. filename 속성은 현재 선택한 파일 이름, fileContent 속성은 저장하거나 불러올 파일의 내용입니다.

 Q_PROPERTY(QDeclarativeListProperty<File> files READ files CONSTANT )<code>

files 목록 속성은 디렉터리에 있는 파일 목록을 포함합니다. Directory 클래스는 올바르지 않은 텍스트 파일을 제외하며, txt 확장자가 있는 파일만 텍스트로 취급합니다. C에서 [http://doc.qt.nokia.com/4.7/qdeclarativelistproperty.html로 QDeclarativeListProperty] 선언한 목록은 QML에서 [http://doc.qt.nokia.com/4.7/qlist.html로 QList] 사용할 수 있습니다. 템플릿에 있는 개체는 [http://doc.qt.nokia.com/4.7/qobject.html에서 QObject] 상속되어야 하므로, File 클래스 역시 [http://doc.qt.nokia.com/4.7/qobject.html의 QObject] 자식입니다. Directory 클래스에서 파일 이름 목록은 [http://doc.qt.nokia.com/4.7/qobject.html QList] m_fileList에 저장됩니다.
class File : public QObject{
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
};
 속성은 QML에서 Directory 구성 요소의 속성으로 사용될  있습니다. id 속성의 본체를 C 구현할 필요가 없습니다.
Directory{
id: directory
filesCount
filename
fileContent
files
files[0].name
}
QML 자바스크립트의 문법과 구조를 사용하므로 파일 목록을 가져올  자바스크립트의 문법을 사용할  있습니다.  번째 파일 이름을 가져오려면 files[0].name 호출하면 됩니다.

QML에서는 일반적인 C''+ 함수도 호출할  있습니다. 파일 읽기  저장 함수는 C++ 구현되어 있으며 [http://doc.qt.nokia.com/4.7/qobject.html#Q_INVOKABLE Q_INVOKABLE] 매크로를 사용합니다. 또 다른 방법으로 함수를 슬롯으로 선언하고 QML에서 사용할 수 있습니다.
Directory.h:
Q_INVOKABLE void saveFile();
Q_INVOKABLE void loadFile();
Directory 클래스는 디렉터리 내용이 바뀌었을  다른 개체에 알려 줘야 합니다.  기능은 시그널로 구현되었습니다. 앞서 이야기한 대로 QML 시그널의 핸들러는 시그널 이름 앞에 on 붙어 있는 함수입니다. 호출되는 시그널의 이름은 directoryChanged이며, 디렉터리 내용이 변경될 때마다 호출됩니다. 새로 고치는 과정은 디렉터리 목록을 다시 가져오고 올바른 파일 목록을 갱신하는 것입니다. onDirectoryChanged 시그널 핸들러에 동작을 붙여 QML 항목에도 알릴  있습니다.

list 속성을  알아봅시다. list 속성은 콜백 함수를 사용하여 내용에 접근하고 수정합니다. list 속성은 QDeclarativeListProperty<File> 형식을 갖습니다. list 접근할  접근하는 함수는 QDeclarativeListProperty<File> 형식을 반환해야 합니다. 템플릿 형식 File QObject 자식 클래스여야 합니다. [http://doc.qt.nokia.com/4.7/qdeclarativelistproperty.html를 QDeclarativeListProperty] 만들려면 list의 접근자와 수정자에 생성자를 함수 포인터로 전달해야 합니다. 이 예제에서는 QList인 list 역시 File 포인터의 목록이 되어야 합니다.

[http://doc.qt.nokia.com/4.7/qdeclarativelistproperty.html QDeclarativeListProperty] 생성자의 생성자와 Directory 구현은 다음과 같습니다:
QDeclarativeListProperty ( QObject object, void * data, AppendFunction append, CountFunction count = 0, AtFunction at = 0, ClearFunction clear = 0 ) QDeclarativeListProperty<File>( this, &m_fileList, &appendFiles, &filesSize, &fileAt, &clearFilesPtr );
생성자는 목록에 항목을 추가하고, 항목 개수를 돌려주고, 인덱스를 사용하여 항목을 가져오고, 목록을 비우는 함수 포인터를 인자로 받습니다. 항목 추가 함수는 필수입니다. 함수 포인터는 [http://doc.qt.nokia.com/4.7/qdeclarativelistproperty.html#AppendFunction-typedef AppendFunction], [http://doc.qt.nokia.com/4.7/qdeclarativelistproperty.html#CountFunction-typedef CountFunction], [http://doc.qt.nokia.com/4.7/qdeclarativelistproperty.html#AtFunction-typedef AtFunction], [http://doc.qt.nokia.com/4.7/qdeclarativelistproperty.html#ClearFunction-typedef과 ClearFunction] 같은 형태여야 합니다.
void appendFiles(QDeclarativeListProperty<File> * property, File * file)
File* fileAt(QDeclarativeListProperty<File> * property, int index)
int filesSize(QDeclarativeListProperty<File> * property)
void clearFilesPtr(QDeclarativeListProperty<File> *property)
파일 대화상자를 간단하게 하기 위해서 Directory 클래스는 .txt 확장자를 가지는 파일만 표시합니다. 또한 저장하는 텍스트 파일 역시 확장자가 .txt여야 합니다. Directory 클래스는 [http://doc.qt.nokia.com/4.7/qtextstream.html을 QTextStream] 사용하여 파일을 읽고 씁니다.

Directory 구성 요소를 사용하여 파일 목록을 가져올  있고, 프로그램 디렉터리에 있는 파일 개수, 파일 이름과 내용을 가져올  있고, 디렉터리 내용이 바뀔 때마다 알림을 발생합니다.

플러그인을 빌드하려면 cppPlugins.pro 프로젝트 파일을 사용하며 qmake 실행시키고, make 실행하여 빌드한 다음 plugins 디렉터리에 플러그인을 설치하십시오.

=== QML에서 플러그인 가져오기 ===

qmlviewer 도구는 프로그램과 같은 디렉터리에 있는 파일을 가져옵니다. qmldir 파일을 만들어서 가져올 QML 파일의 위치를 지정할  있습니다. qmldir 파일은 플러그인과 다른 자원의 위치도 지정할  있습니다.
qmldir:

Button ./Button.qml

FileDialog ./FileDialog.qml
TextArea ./TextArea.qml
TextEditor ./TextEditor.qml
EditMenu ./EditMenu.qml
plugin FileDialog plugins
방금 만든 플러그인의 이름은 FileDialog이며, 프로젝트 파일의 TARGET 지정한 이름입니다. 컴파일한 플러그인은 plugins 디렉터리에 있습니다.

=== 파일 메뉴에 파일 대화상자 통합하기 ===

FileMenu FileDialog 구성 요소를 표시해야 하며, 디렉터리에 있는 텍스트 파일 목록을 표시하여 사용자가 파일을 선택할  있도록 해야 합니다. 저장, 불러오기, 새로 만들기 단추와 동작을 연결해야 합니다. FileMenu 항목은 키보드로 파일 이름을 입력받을  있는 텍스트 상자가 필요합니다.

FileMenu.qml 파일에서 Directory 구성 요소를 사용하며, FileDialog 구성 요소에 디렉터리 내용이 바뀌었음을 알려 줍니다. 시그널 핸들러 onDirectoryChanged에서 알림이 이루어집니다.
FileMenu.qml:

Directory{

id:directory
filename: textInput.text
onDirectoryChanged: fileDialog.notifyRefresh()
}
프로그램을 간단하게 하기 위해서, 파일 대화상자는 항상 표시되고, .txt 확장자가 아닌 파일은 표시되지 않습니다.
FileDialog.qml:

signal notifyRefresh()

onNotifyRefresh: dirView.model = directory.files
FileDialog 구성 요소는 files 속성의 값을 읽어서 디렉터리 내용을 표시합니다. files 속성은 [http://doc.qt.nokia.com/4.7/qml-gridview.html의 GridView] 값으로 사용되며, 데이터를 모눈에 맞추어 표시합니다. 모델의 모양을 설정할 수 있으며, 기본적으로 파일 대화상자는 텍스트가 가운데 정렬된 모눈을 만듭니다. 파일 이름을 누르면 현재 선택한 파일을 강조하는 사각형이 표시됩니다. FileDialog는 notifyRefresh 신호를 감지하여 디렉터리 내용을 새로 고칩니다.
FileMenu.qml:

Button{

id: newButton
label: "New"
onButtonClick:{
textArea.textContent = ""
}
}
Button{
id: loadButton
label: "Load"
onButtonClick:{
directory.filename = textInput.text
directory.loadFile()
textArea.textContent = directory.fileContent
}
}
Button{
id: saveButton
label: "Save"
onButtonClick:{
directory.fileContent = textArea.textContent
directory.filename = textInput.text
directory.saveFile()
}
}
Button{
id: exitButton
label: "Exit"
onButtonClick:{
Qt.quit()
}
}

이제 FileMenu와 동작이 연결되었습니다. saveButton은 TextEdit의 텍스트를 directory의 fileContent 속성으로 복사하고, 편집 가능한 텍스트 입력 상자에서 파일 이름을 읽어옵니다. 그 다음 saveFile() 함수를 호출하여 실제 파일에 씁니다. loadButton 역시 비슷한 과정을 수행하며, new 동작은 TextEdit의 내용을 비웁니다.

EditMenu에 있는 단추 역시 TextEdit의 복사, 붙여넣기, 전체 선택과 연결되었습니다.

http://doc.qt.nokia.com/4.7/images/qml-texteditor5_filemenu.png

http://doc.qt.nokia.com/4.7/images/qml-texteditor5_newfile.png