Pull to refresh

Работаем с моделями в QML

Reading time9 min
Views8.8K

В продолжении предыдущего поста, хочется рассказать ещё немного о новой технологии от Qt Software и Nokia. Напомню, что QML — это новый язык описания пользовательского интерфейса, призванный упростить разработку современных приложений и наполнить их движением.
В этой статье мне хотелось бы немного рассказать о том, как в QML работает парадигма MVC

Так, что ничего не нужно будет компилировать, для изучения будет достаточно иметь сборку Креатора с qmlviewer'ом, она есть на ftp троллей.



Давайте попробуем сделать нечто похожее на то, что изображено на скриншоте:


В Креаторе можно создавать свои qml проекты, а также импортировать уже существующие каталоги с qml файлами.


В результате чего появится файл *.qmlproject, который можно открывать Креатором и работать уже не с отдельными qml файлами, а с целыми проектами.
Для работы базовых типов необходимо подключать в qml файлах модуль Qt при помощи директивы import.
Каждый qml файл является модулем, по умолчанию автоматически импортируются все файлы, которые находятся в текущем каталоге
Первым делом давайте создадим кнопку:
Button.qml
import Qt 4.6

Item {
  id: container

  signal clicked

  property string text
  property bool keyUsing: false

  BorderImage {
    id: buttonImage
    source: "images/toolbutton.sci"
    width: container.width; height: container.height
  }
  BorderImage {
    id: pressed
    opacity: 0
    source: "images/toolbutton.sci"
    width: container.width; height: container.height
  }
  MouseRegion {
    id: mouseRegion
    anchors.fill: buttonImage
    onClicked: { container.clicked(); }
  }
  Text {
    id: btnText
    color: if(container.keyUsing){"#DDDDDD";} else {"#FFFFFF";}
    anchors.centerIn: buttonImage; font.bold: true
    text: container.text; style: Text.Raised; styleColor: "black"
    font.pixelSize: 12
  }
  states: [
    State {
      name: "Pressed"
      when: mouseRegion.pressed == true
      PropertyChanges { target: pressed; opacity: 1 }
    },
    State {
      name: "Focused"
      when: container.focus == true
      PropertyChanges { target: btnText; color: "#FFFFFF" }
    }
  ]
  transitions: Transition {
    ColorAnimation { target: btnText; }
  }

  width: (btnText.width + 20)
}


* This source code was highlighted with Source Code Highlighter.

Массив states описывает различные состояния кнопки, в данном случае их всего два, нажата или отпущена. В самих состояниях указываются условия, при которых объект переходит в эти состояния. Действия, происходящие в процессе перехода между состояниями описываются в массиве transitions. Также кнопка будет испускать сигнал clicked, который может принимать и обрабатывать любой объект.
Теперь у нас есть модуль кнопка, который можно использовать в других модулях:
contactlist.qml
...
      Button {
        id: tagsBtn
        anchors.right: parent.right;
        anchors.rightMargin: 5;
        y: 1;
        height: (parent.height - 3)
        text: "Все метки"
      }
...

* This source code was highlighted with Source Code Highlighter.

Точно таким же образом в примере реализован тулбар и строка поиска.
Давайте теперь перейдем собственно к модели:
В данном примере в качестве модели используется лента последних добавленных фотографий на flickr'а. Сайт предоставляет эту информацию в виде rss. В qml'е есть специальная модель для обработки xml данных, которая работает на основе xpath.
RssModel.qml
import Qt 4.6

XmlListModel {
  property string tags : ""

  source: "http://api.flickr.com/services/feeds/photos_public.gne?"+(tags ? "tags="+tags+"&" : "")+"format=rss2"
  query: "/rss/channel/item"
  namespaceDeclarations: "declare namespace media=\"http://search.yahoo.com/mrss/\";"

  XmlRole { name: "title"; query: "title/string()" }
  XmlRole { name: "imagePath"; query: "media:thumbnail/@url/string()" }
  XmlRole { name: "url"; query: "media:content/@url/string()" }
  XmlRole { name: "description"; query: "description/string()" }
  XmlRole { name: "tags"; query: "media:category/string()" }
  XmlRole { name: "photoWidth"; query: "media:content/@width/string()" }
  XmlRole { name: "photoHeight"; query: "media:content/@height/string()" }
  XmlRole { name: "photoType"; query: "media:content/@type/string()" }
  XmlRole { name: "photoAuthor"; query: "author/string()" }
  XmlRole { name: "photoDate"; query: "pubDate/string()" }
}

* This source code was highlighted with Source Code Highlighter.

Теперь у нас в руках находится модель, которая содержит почти всю информацию из ленты flicker'а. Теперь давайте попробуем делегата, который будет отвечать за отображение элемента из модели, но не забудем сделать так, чтобы фон соседних элементов отличался по яркости.
ListDelegate.qml
import Qt 4.6

Component {
  Item {
    id: wrapper;
    width: parent.width;
    height: (avatar.y + avatar.height + 15)

    Rectangle {
      color: "black"
      opacity: index % 2 ? 0.2 : 0.3
      height: wrapper.height
      width: wrapper.width
      y: 1
    }

    Rectangle {
      id: avatarBorder
      width: (avatar.width + 2); height: (avatar.height + 2);
      color: "transparent";
      smooth: true

      Image {
        id: avatar
        width: 32; height: 32
        source: imagePath
        anchors.centerIn: parent
      }

      anchors.left: wrapper.left
      anchors.leftMargin: 5
      anchors.verticalCenter: parent.verticalCenter
    }
    Text {
      text: name
      color: "white"
      font.bold: true
      style: "Raised"; styleColor: "black"

      anchors.left: avatarBorder.right
      anchors.leftMargin: 10
      anchors.top: avatarBorder.top
    }
    Text {
      text: type
      color: "#CCC"
      font.bold: true
      style: "Raised"; styleColor: "black"

      anchors.left: avatarBorder.right
      anchors.leftMargin: 10
      anchors.bottom: avatarBorder.bottom
    }
  }
}

* This source code was highlighted with Source Code Highlighter.

Как мы видим, доступ к элементам модели осуществляется очень просто.
Теперь давайте соберём всё это вместе и отобразим при помощи ListView'а, который выводит информацию в виде простого списка и имеет такой приятный бонус, как кинетическая прокрутка.
Создадим объекты нужных нам типов и зададим view'у наши модель и делегата:
contactlist.qml
...  
  RssModel {
      id: rssModel
    }

    ListModel {
      id: proxyModel
    }

    ListDelegate {
      id: listDelegate
    }

    ListView {
      id: contactListView;
      z: 0
      model: contactlistModel; delegate: listDelegate;
      highlight : highlight;highlightFollowsCurrentItem: true
      focus: true
      width: parent.width;
      cacheBuffer: 100;
      anchors.top: topToolBar.bottom
      anchors.bottom: searchBar.top
    }
...


* This source code was highlighted with Source Code Highlighter.

Помимо модели и делегата мы задали view'у элемент под названием highlight, он отвечает за подсвечивание текущего элемента:
    Component {
      id: highlight
      Rectangle {
        width: contactListView.currentItem.width
        height: contactListView.currentItem.height
        color: "white"; radius: 5; opacity: 0.3
        y: SpringFollow {
          source: contactListView.currentItem.y
          spring: 3
          damping: 0.2
        }
      }
    }


* This source code was highlighted with Source Code Highlighter.

Свойство SpringFollow позволяет задавать поведение подсветки при следовании за текущим элементом.
На последок давайте попробуем организовать поиск по модели: Для этого создадим обычную модель:
ListModel {
  id: MyListElementsModel
  ListElement {
    name: "Polly"
    type: "Parrot"
    imagePath: "images/avatar.png"
    age: 12
    size: "Small"
  }
...


* This source code was highlighted with Source Code Highlighter.

Поиск будет осуществляться при помощи javascript'а, складывая все поля, удовлетворяющие условиям, в промежуточную proxyModel
      onConfirmed: {
        proxyModel.clear();
        for (var i=0;i!=contactlistModel.count;i++) {
          var item = contactlistModel.get(i);
          if (item.name.search(searchBar.text) != -1) {
            proxyModel.append({"name": item.name, "type": item.type, "imagePath": item.imagePath});
          }
        }
        contactListView.model = proxyModel;
      }
      onCancelled: {
        contactListView.model = contactlistModel;
      }


* This source code was highlighted with Source Code Highlighter.

Скрипт поиска будет запускаться, когда на слот onConfirmed придет сигнал confirmed от строки поиска. В случае же активации слота onCancelled view'у будет возращена исходная модель.
Скриншот, демонстрирующий работу поиска:


Оба примера вы можете свободно скачать и пощупать у меня с сайта:
Пример с xml моделью
Пример с поиском
Запускать их можно или открыв проект в Креаторе или через программу qmlviewer, просто указав ей файл contactlist.qml
На сегодня всё. На последок хочу сказать, что с qml ваши волосы будут гладкими и шелковистыми ;)
Tags:
Hubs:
Total votes 47: ↑45 and ↓2+43
Comments35

Articles