Ім'я файлу: 1_Qt+QML.doc
Розширення: doc
Розмір: 622кб.
Дата: 04.04.2021
скачати

Qt + QML на простом примере


Qt*

Qt является удобным и гибким средством для создания кросс-платформенного программного обеспечения. Входящий в его состав QML предоставляет полную свободу действий при создании пользовательского интерфейса.
Об удобстве использования связки Qt и QML уже говорилось не раз, поэтому не буду дальше распространяться о плюсах, минусах, а приведу, шаг за шагом, пример простого Qt приложения.

Это будет минималистичное приложение, cмысл его простой — при нажатии на любую клавишу на клавиатуре на экране будет появляться случайное изображение и будет проигрываться, опять же, случайный звуковой эффект.

Пользовательский интерфейс будет полностью реализован на QML, программная часть на Qt.

Для тех, у кого Qt еще не установлен, заходим на страницу загрузки qt.nokia.com/downloads/ и в разделе "Qt SDK: Complete Development Environment" скачиваем бинарники для своей платформы. И, собственно, устанавливаем.

Запускаем Qt Creator, выбираем пункт меню Файл -> Новый файл или проект. В открывшемся окне Проект Qt C++ -> Gui приложение Qt, далее кнопка Выбрать.



В новом окне вводим название проекта, указываем путь к проекту, жмем Далее.

В следующем окне снимаем галочку Создать форму, она нам не пригодиться, Далее. И в последнем просто нажимаем кнопку Завершить. Все, каркас нашего приложения создан.



Первым делом добавим модуль qt-declarative к нашему проекту, для этого, в файле проекта (4Toddler.pro), к строке

QT += core gui


добавим declarative

QT += core gui declarative


Далее изменим базовый класс для нашего главного окна, заменим QMainWindow на QDeclarativeView и включим заголовочный файл QDeclarativeView

#include

class MainWindow : public QDeclarativeView
{
  ...
}


От реализации конструктора отрежем, ставшую ненужной, инициализацию базового класса QMainWindow(parent).

Если сейчас собрать и запустить проект то мы увидим пустое окно. Так и должно быть, т.к. мы еще не создали и не инициализировали Qml интерфейс.

Добавим в проект новый QML файл, для этого щелкаем правой клавишей по проекту



Добавить новый..., далее выбираем раздел Qt, шаблон Файл Qt QML. Даем ему имя main, затем Далее и Завершить.



Мастер создал нам файл, содержащий один элемент Rectangle, он и будет являться основным элементом для нашего пользовательского интерфейса. Добавим несколько новых свойств и зададим их значение

Rectangle
{
  // Идентификатор, по нему будет происходить
  // обращение к свойствам этого элемента
  id: canvas;

  // Цвет фона, черный
  color: "black"

  // Изменять размер под размеры
  // родительского элемента
  anchors.fill: parent

  // Будет получать фокус ввода
  focus: true
}


Пока ничего особенного, просто черный фон. Добавим код загрузки QML файла, чтобы посмотреть, что у нас получилось. Для этого добавим нашему окну новый метод

void MainWindow::Init()
{
  // Путь к папке, содержащей QML файлы
  QString contentPath;

#ifdef QT_DEBUG
  // В отладочной версии это абсолютный путь к папке проекта
  contentPath = "D:/MyProjects/QT/4Toddler";
#else
  // В релизе это путь к папке, в которой расположено приложение
  contentPath = QApplication::applicationDirPath();
#endif

  setFocusPolicy(Qt::StrongFocus);
  // Изменять размеры QML объекта под размеры окна
  // Возможно делать и наоборот,
  // передавая QDeclarativeView::SizeViewToRootObject
  setResizeMode(QDeclarativeView::SizeRootObjectToView);

  // Загрузить QML файл
  setSource(QUrl::fromLocalFile(contentPath + "/main.qml"));
}


Теперь заменим в файле main.cpp строку

int main(int argc, char *argv[])
{
  ...
  w.show();
  ...
}


на

int main(int argc, char *argv[])
{
  ...
  w.showFullScreen();
  ...
}


Окно будет разворачиваться на весь экран. Прежде, чем запускать приложение, давайте добавим кнопку, с помощью которой можно будет закрыть окно. Забегая вперед скажу, кнопок в окне будет две, для того, чтобы не писать несоклько раз один и тот же код добавим к проекту новый QML файл, и назовем его WindowButton.

Элемент WindowButton мы будем использовать повторно, изменяя лишь определенные свойства у каждого экземпляра. Кнопки у нас будут выполнены в виде иконок, каждой из них мы будем задавать путь к файлу иконки и изменять обработчик нажатия левой клавишей мыши. Ниже приведен готовый код элемента с комментариями

Image
{
  // Идентификатор элемента
  id: button

  // Область, обрабатывающая "мышиные" сообщения
  MouseArea
  {
    // Действует в пределах всего
    // элемента Image
    anchors.fill: parent

    id: mouseArea

    // При нажатии вызвать метод callback
    onClicked: callback()
  }
}


Добавим пару кнопок к нашему окну

// Элемент позволяющий
// распологать элементы горизонтально
Row
{
  // Правый край элемента выравнивается
  // по правому краю родительского элемента
  anchors.right: parent.right;
  // Отступ справа, 4 пикселя
  anchors.rightMargin: 4;
  // Верхний край эелемента выравнивается
  // по верхнему краю родительского элемента
  anchors.top: parent.top;
  // Отступ сверху, 4 пикселя
  anchors.topMargin: 4;

  // Отступ между элементами
  spacing: 4

  WindowButton
  {
    // Кнопка возова диалога "О программе"
    id: about;

    // Путь к файлу с изображением
    // в данном случае иконка лежит в той же папке,
    // что и QML файл
    source: "about.png";

    // Метод, который будет вызываться
    // при нажатии на кнопку левой клавишей мыши
    // onClicked: callback()
    function callback()
    {
    }
  }

  WindowButton
  {
    // Кнопка закрытия окна
    id: exit;

    source: "exit.png";

    function callback()
    {
    }
  }
}

Чтобы то, что мы сделали заработало нам осталось реализовать оба метода callback для каждой кнопки. Для закрытия окна мы вызовем метод Quit, который реализуем в классе окна. Для этого в объявление класс добавим

Q_INVOKABLE void Quit();


Затем реализуем этот метод

void MainWindow::Quit()
{
  QApplication::quit();
}


Осталось сделать этот метод видимым из QML. В метод Init добавим одну единствунную строку, которая сделает экземпляр нашего окна видимым в QML

rootContext()->setContextProperty("window", this);


Обращаться к этому объекту мы сможем по имени — window, имя это произвольное. Добавим реализацию для кнопки закрытия окна

function callback()
{
  window.Quit();
}


Обратите внимание, что вызывать можно только те методы, которые объявлены как Q_INVOKABLE, т.е. от же метод Init, главного окна вызвать не удастся.

Готово, запускаем, видим черный экран, все, что сейчас мы можем сделать это закрыть окно, нажав на кнопку exit. Нажали и видим, что состояние кнопки при наведении курсора и при нажатии никак не меняется, выглядит как «неживая». Оживим ее, добавив состояния:

Image
{
  ...
  states:[
    State
    {
      // Произвольное название
      name: "hovered";
      // Указание на то, когда элемент переходит в это состояние
      // в данном случае когда нажата левая кнопка мыши
      when: mouseArea.pressed;
      // Какие свойства будут изменяться в этом состоянии
      // в данном случае это будет прозрачность
      PropertyChanges { target: button; opacity: 1;}
    },
    State
    {
      name: "normal"
      // В это состояние элемент будет переходить
      // когда левая кнопка мыши не нажата
      when: mouseArea.pressed == false;
      PropertyChanges { target: button; opacity: 0.7; }
    }
   ]
}


Элемент может переходить в определенное состояние как автоматически при выполнении условия, указанного в when, так и вручную, путем изменения свойства state.

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

Image
{
  ...
  Behavior on opacity
  {
    // Анимация с шагом в 100 миллисекунд
    // Раз в 100 миллисекунд прозрачность будет изменяться
    // на 0,1
    NumberAnimation { duration: 100 }
  }
}


Behavior очень полезный элемент для создания анимаций, позволяющий указать то как будет меняться указанное свойство, в данном случае прозрачность кнопки.

Запускаем и смотрим, совсем другое дело, плавный переход от полупрозрачного к непрозрачному состоянию.


Окно о программе будет реализовано полностью на QML. Это будет модальное окно, которое будет появляться при нажатии кнопки about. При щелчке левой клавишей мыши в любом месте окна оно будет исчезать. Добавим новый QML файл About.qml в проект.



Я приведу сразу весь код этого окна с пояснениями

// Главный элемент для диалогового окна
Rectangle
{
  id: about

  // Функция для отображения окна
  // изменяет прозрачность главного элемента
  function show()
  {
    about.opacity = 1;
  }

  // Функция для закрытия окна
  function hide()
  {
    about.opacity = 0;
  }
  
  // Прозрачный задний фон
  color: "transparent"
  // Полностью прозрачен по умолчанию
  opacity: 0
  
  // Ширина и высота устанавливаются равными
  // ширине и высоте родительского элемента
  // в данном случае это элемент с id: canvas
  width: parent.width
  height: parent.height

  // Видимым элемент будет считаться если выполняется условие
  // opacity > 0
  visible: opacity > 0

  // Дочерний элемент, создающий полупрозрачный фон
  Rectangle
  {
    anchors.fill: parent

    opacity: 0.5

    color: "gray"
  }

  // Дочерний элемент создающий который является диалогом
  // "О программе..."
  Rectangle
  {
    id: dialog
    
    // Ширина и высота являются фиксированными
    width: 360
    height: 230

    // Координаты верхнего левого угла вычисляются
    // исходя из размеров самого диалога и родителя
    // так, чтобы окно располагалось в центре
    x: parent.width / 2 - dialog.width / 2;
    y: parent.height / 2 - dialog.height / 2;
    // Задаем z индекс таким, чтобы он был
    // больше z тех элементов, которые должны остаться
    // за диалоговым окном
    z: 10

    border.color: "gray"

    Text
    {
      text: "4 Toddler"

      font.bold: true

      font.pixelSize: 22
      
      // Выравнивание элемента по центру
      anchors.horizontalCenter: parent.horizontalCenter
      anchors.verticalCenter: parent.verticalCenter
    }
  }

  Behavior on opacity
  {
    NumberAnimation { duration: 100 }
  }

  MouseArea
  {
    // Элемент полностью заполняет родительский элемент
    anchors.fill: parent;
    
    // При клике в любом месте прячем окно
    onClicked: hide();
  }
}


Для начала хотелось бы обратить внимание на свойство

width: parent.width


Это не просто присвоение ширины, если в процессе отображения будет меняться ширина родительского элемента, то и ширина дочернего будет перерасчитана. Не знаю как вас, а меня в процессе «ковыряния» QML эта особенность приятно удивила. Так же интересна следующая строка:

visible: opacity > 0


Свойство может быть не только задано, но и вычислено.

Осталось добавить диалог и код для его отображения при нажатии кнопки about. В файл Main.qml добавим код, в конце элемента canvas

Rectangle
{
    id: canvas
  ..
  About
  {
    id: aboutDlg
  }
}


Для того, чтобы окно отображалось добавим строку

aboutDlg.show();


в функцию callback кнопки about

WindowButton
{
  id: about;
  ...
  function callback()
  {
    aboutDlg.show();
  }
}


Теперь добавим, собственно основной функционал. Начнем с отображения случайной картинки при нажатии любой клавиши. Картинка будет являться эелементом Image, определим этот элемент в отдельном файле. Добавим в проект файл Block.qml

Image
{
    id: block;

  // Новое свойство объекта, необходимое
  // для изменения состояния при удалении объекта
    property bool remove: false
  // При добавлении объекта
    property bool show: false
  
    opacity: 0;
    fillMode: Image.Stretch;

  states: [
    State
    {
      // Состояние, в которое переходит объект
      // тогда, когда нам нужно его удалить
      name: "remove"; when: remove == true;
      PropertyChanges { target: block; opacity: 0 }
      StateChangeScript { script: block.destroy(1000); }
    },
    State
    {
      // Состояние, в которое переходит объект
      // тогда, когда нам нужно его отобразить
      name: "show"; when: show == true;
      PropertyChanges { target: block; opacity: 1 }
    }
  ]
  
    Behavior on opacity { NumberAnimation { duration: 300 } }
}


При нажатии любой клавиши на клавиатуре будет отображаться блок с произвольной картинкой. Добавим в проект новый файл main.js. В нем, мы определим обработчик нажатия клавиши на клавиатуре.

// Шаблон для создания новых элементов
var component = Qt.createComponent("block.qml");

// Максимальное количество элементов
var maxBlocksCount = 10;

// Массив, в котором будут храниться все эелементы
var blocksArray    = new Array();

// Функция обработчик нажатия клавиши
function handleKey()
{
  // Координата x - случайно число от 0 до ширины окна
  var x = Math.floor(Math.random() * canvas.width);
  // Координата y - случайно число от 0 до ширины окна
  var y = Math.floor(Math.random() * canvas.height);

  // Вызов функции, которая создаст новый элемент
  // с указанными координатами
  createNewBlock(x, y);
}

// Создание нового элемента
function createNewBlock(x, y)
{
  if(component.status != Component.Ready)
  {
    return false;
  }

  // Удалить лишние элементы
  if(blocksArray.length > maxBlocksCount)
  {
    removeAllBlocks();
  }

  var newBlock = component.createObject(canvas);

  if(newBlock == null)
  {
    return false;
  }

  // Путь к файлу иконки доступен через свойство главного
  // окна randomIcon
  var iconFile = window.randomIcon;

  newBlock.source = ("Icons/" + iconFile);

  newBlock.x = x;
  newBlock.y = y;

  // Переводим элемент в состояние show
  newBlock.show = true;

  blocksArray.push(newBlock);

  // Проигрываем случайный звуковой эффект
  window.PlaySound();

  return true;
}

// Удаление всех добавленных элементов
function removeAllBlocks()
{
  for(var i = 0; i < blocksArray.length; ++i)
  {
    blocksArray[i].remove = true;
  }

  while(blocksArray.length != 0)
  {
    blocksArray.pop();
  }
}


Как видно из кода нам еще следует реализовать свойство randomIcon и функицию PlaySound главного окна.

Добавим свойство в объявление класса MainWindow

Q_PROPERTY(QString randomIcon READ RandomIcon)


И объявление функции

QString RandomIcon();


Затем реализацию:

QString MainWindow::RandomIcon()
{
  QStringList iconFilesList;
  QString searchPath = m_ContentPath + "/Icons/";

  QDir directory = QDir(searchPath);
  QStringList filters;
  filters << "*.png";
  directory.setNameFilters(filters);
  // Получаем список файлов с расширением png
  iconFilesList = directory.entryList(QDir::AllEntries);

  // Получаем случайный индекс элемента
  int fileIdx = qrand() % iconFilesList.count();
  
  // Возвращаем название файла
  return iconFilesList.at(fileIdx);
}


Теперь добавим в заголовочный файл функцию для проигрывания звукового эффекта

Q_INVOKABLE void PlaySound();


и реализацию

void MainWindow::PlaySound()
{
  QStringList soundFilesList;
  QDir directory = QDir(m_ContentPath + "/Sounds/");
  QStringList filters;
  filters << "*.wav";
  directory.setNameFilters(filters);

  // Получаем список файлов с расширением wav
  soundFilesList = directory.entryList(QDir::AllEntries);
  // Получаем случайный индекс элемента
  int fileIdx = qrand() % soundFilesList.count();

  // Получаем название файла
  QString soundFile = m_ContentPath + "/Sounds/" + soundFilesList.at(fileIdx);
  // Проигрываем файл
  QSound::play(soundFile);
}


Почти все, осталось добавить обработчик нажатия клавиш в наш корневой элемент вызов фунции создания нового элемента. В начален файла main.qml сделаем видимым наш скрипт в файле main.qml

import Qt 4.7
import "main.js" as Main

и сам обработчик внутри элемента canvas

Rectangle
{
    id: canvas
  ...
  Keys.onPressed: { if(event.isAutoRepeat == false) { Main.handleKey(); } }


На этом все — можем запускать и любоваться.



Как я и обещал программа является простой, но, на мой взгляд, это достаточно интересная «игровая» площадка для тех, кто только начинает изучать QML. Нет предела совершенству, кто знает, может кто-нибудь разовьет ее во что-то более стоящее.

Архив с проектом можно скачать здесь

учшие приёмы Qt Quick: Компоненты


Блог компании Microsoft Lumia

QML предоставляет удобный способ разбиения кода под названием «Компоненты». Самым простым способом создания компонента, который можно будет в последствии использовать многократно, является добавление нового файла в рабочую директорию главного QML-файла.

Example.qml:

import QtQuick 1.0

Rectangle {

}


main.qml:

import QtQuick 1.0

Example {

}

Также, компоненты можно упаковывать как модули (Qt Components являются таким модулем) и публиковать в виде плагинов. Этот пост посвящён использованию компонентов для написания чистого и легко поддерживаемого QML-кода.

Создание новых компонентов



Первый пример показал простоту создания дополнительных компонентов, так что не бойтесь их использовать.

Не делайте написание кода, пригодного для многократного использования, своей первоочередной целью. Стремитесь к инкапсулированию деталей реализации и уменьшению связности (decoupling) компонентов. Компоненты должны быть небольшими. Следуя этим правилам вы автоматически придёте к коду, который в последствии можно будет использовать многократно.

Давайте посмотрим на этот пример простых аналоговых часов:

скачать пример / посмотреть онлайн
main.qml:

import QtQuick 1.0

// Покажем текущее время в аналоговых часах.

Rectangle {

id: root

width: 320

height: 320

property variant now: new Date()

Timer {

id: clockUpdater

interval: 1000 // обновляем часы каждую секунду

running: true

repeat: true

onTriggered: {

root.now = new Date()

}

}

Clock {

id: clock

anchors.centerIn: parent

hours: root.now.getHours()

minutes: root.now.getMinutes()

seconds: root.now.getSeconds()

}

}


Clock.qml:

import QtQuick 1.0

// Аналоговые часы, способные отображать часы, минуты и секунды.

Rectangle {

id: root

width: 262 // минимальная ширина

height: 262 // минимальная высота

// public:

property int hours: 0

property int minutes: 0

property int seconds: 0

// private:

Item {

id: impl

Image {

id: face

source: "images/face.png"

Image {

id: shorthand

source: "images/shorthand.png"

smooth: true

rotation: root.hours * 30

}

Image {

id: longhand

source: "images/longhand.png"

smooth: true

rotation: root.minutes * 6

}

Image {

id: thinhand

source: "images/thinhand.png"

smooth: true

rotation: root.seconds * 6

}

Image {

id: center

source: "images/knob.png"

}

}

}

}

Этот код содержит компонент Clock, который при запуске выглядит так, как показано на скриншоте снизу. Несмотря на то, что он используется в приложении единожды, был смысл выделить его из основного файла.



Во-первых, это делает простой и понятной оставшуюся в файле main.qml логику: таймер, обновляющий часы, минуты и секунды компонента Clock — это всё, что разработчику необходимо видеть в main.qml, если он захочет добавить ему функциональности.

Во-вторых, наш компонент Clock может не беспокоиться за собственное расположение в окне. Предположим, есть элемент Row, использующий наш компонент Clock N-раз. Если бы в корневом элементе компонента Clock был код 'anchors.fill: parent', мы бы не могли использовать его экземпляры в элементе Row: каждый экземпляр Clock занимал бы весь width_row, вместо width_row / N. Именно поэтому QML запрещает использование большинства якорей (anchors) в элементах, помещаемых в элемент Row. Если мы хотим чтобы наш компонент Clock оставался пригодным для многократного использования, мы не должны строить многочисленные предположения относительно его будущего использования. Резюмируя, корневой элемент компонента не должен содержать якоря к своему родителю или использовать жёстко заданные менеджеры размещения (layouts).

В то же время компонент Clock задает фиксированные значения своим длине и ширине. С семантической точки зрения, это размеры нашего компонента по умолчанию. Они не ограничивают использование нашего компонента, ведь размер его экземпляров можно изменить при необходимости. Компоненты с нулевыми размерами считаются невидимыми элементами, поэтому установка ненулевых размеров по умолчанию позволяет избежать глупых ошибок.

Также существуют другие, менее очевидные достоинства, такие как создание составных (composed) элементов и инкапсуляция деталей реализации.

Создание составного элемента (назовём его ComposedElement) из простых элементов ElementA, ElementB и ElementC упрощает добавление новых свойств и действий к элементам. Мы можем добавить новые элементы ElementD и ElementE к нашему ComposedElement без необходимости изменения ElementA, ElementB или ElementC. Наши простые элементы изолированы друг от друга и, поэтому, не могут просто так сломаться, если один из них вдруг поменяется.

Компоненты могут быть разделены на публичную и приватную части, так же как это делается в классах C++ и Java. Публичный API компонента есть сумма всех его свойств и методов, определённых в корневом элементе (включая унаследованные им свойства). Это значит, что такие свойства могут быть изменены, а такие методы могут быть вызваны пользователями компонента.

Любое свойство или метод, определённые во вложенном элементе (не корневом), могут считаться полностью приватным API. Это позволяет инкапсулировать детали реализации и должно, в итоге, стать обычным делом при создании компонентов разработчиками.

Чтобы доказать пользу от инкапсуляции, мы можем удалить внутренний элемент 'impl' из Clock.qml и запустить приложение вновь (например, через "$ qmiviewer main.qml"). Никаких новых ошибок не будет видно, так как публичный API компонента Clock не был изменён. Это значит, что мы можем свободно менять 'impl', зная, что никаких сторонних эффектов от таких изменений для других компонентов не появится.

Мы даже можем расширить эту идею и позволить Clock.qml загружать какой-либо элемент 'impl' динамически, в зависимости от ситуации. Это вводит концепцию полиморфизма в QML; реализация подобного механизма остаётся читателю в качестве упражнения.

Если у нас есть хорошо спроектированный, минимальный публичный API для каждого компонента, мы можем сосредоточиться на разработке интерфейсов, а не конкретной реализации.

Повторное использование компонентов



Проверим, можем ли мы в действительности многократно использовать компонент Clock.qml. Он прекрасно подходит для создания часов, отображающих мировое время. В этом случае, нам не нужна секундная стрелка на них. Мы можем слегка изменить поведение Clock.qml, чтобы последний не отображал часы, минуты или секунды, если их значение меньше нуля. Для элемента Image с id = thinhand мы можем связать свойство visible с количеством секунд:

visible: root.seconds > -1

Мы не изменяли публичный API. Мы также полагаем, что использование в аналоговых часах отрицательных значений для секунд всё равно не имеет никакого смысла. Поэтому мы уверены, что это изменение не повлияет на работу существующих приложений, которые используют компонент Clock.

В нашем примере с мировыми часами мы, помимо всего прочего, можем отображать локальную информацию о погоде. Мы можем использовать один из доступных API погодных сервисов вместе с XmlListModel, который позволяет декларативно извлекать данные из API. Таймер, который ранее использовался только для обновления времени на часах, теперь будет использован и для обновления данных о погоде раз в час. Обратите внимание на то, как вводится сигнал обновления, который соединяется с функцией обновления данных XmlListModel.

Изменённый пример отображает мировые часы и локальную информацию о погоде для трёх городов.



Компонент Clock интегрируется тривиально. Мы отключили секундную стрелку и использовали UTC для значений времени вместе со смещением для конкретного города. Это сработало, так как Clock был спроектирован как простой элемент интерфейса. Если бы таймер у нас был внутри компонента (вместо того, чтобы давать изменять часы, минуты и секунды как свойства компонента), это бы сильно усложнило работу. К сожалению, main.qml значительно вырос в размере. Элемент Repeater внёс свой вклад в сложность проекта, равно как и массивы utcOffsets и cities.

скачать пример / посмотреть онлайн
main.qml:

Rectangle {

...

property variant cities: ["Berlin", "Helsinki", "San Francisco"]

property variant utcOffsets: [1, 2, -8]

property variant now: new Date()

signal refresh()

Timer {

...

property int hours

onTriggered: {

hours = root.now.getHours()

root.now = new Date()

// Получение данных о погоде каждый час:

if (hours != root.now.getHours()) {

root.refresh()

}

}

}

Row {

anchors.horizontalCenter: parent.horizontalCenter

Repeater {

model: root.cities

// Отображаем аналоговые часы с локальными временем и погодой для каждого города.

Rectangle {

id: current

width: 262 // минимальная ширина

height: 320 // минимальная высота

property string city: cities[index]

property int utcOffset: utcOffsets[index]

XmlListModel {

id: cityQuery

...

}

ListView {

model: cityQuery

anchors.fill: parent

delegate:

Item {

Clock {

id: clock

anchors.left: parent.left

anchors.top: parent.top

// Убедимся, что UTC со смещением никогда не станет отрицательным,

// иначе часы не будут отображаться:

hours: root.now.getUTCHours() + current.utcOffset + 24

minutes: root.now.getMinutes()

seconds: -1

}

Row {

...

Image {

id: icon

source: "http://www.google.com" + model.iconUrl

}

Text {

id: label

text: current.city + ", "+ model.temperature

+ "°C\n" + model.humidity

+ "\n" + model.windCondition

}

}

}

}

}

}

}

}

Наша цель — сделать main.qml простым для понимания. В то же время, мы не хотим делать Clock.qml излишне сложным, потому что, в таком случае, его будет трудно использовать как простые аналоговые часы. Поэтому мы создали новый компонент, состоящий из сбора данных о погоде и компонента Clock. Он содержит таймер, логику обновления, XmlListModel и интеграцию Clock. Вместо того, чтобы напрямую задавать здесь массивы utcOffsets и cities, мы добавляем новые публичные свойства для компонента WeatherWorldClock:

// public:

property string city: ""

property int utcOffset: 0

Мы можем удалить эти массивы и Repeater из main.qml.

скачать пример / посмотреть онлайн
main.qml:

import QtQuick 1.0

// Отображает время и погоду для выбранных городов.

Rectangle {

id: root

width: 786

height: 320

Row {

id: cities

anchors.fill: parent

anchors.horizontalCenter: parent.horizontalCenter

WeatherWorldClock {

city: "Berlin"

utcOffset: 1

}

WeatherWorldClock {

city: "Helsinki"

utcOffset: 2

}

WeatherWorldClock {

city: "San Francisco"

utcOffset: -8

}

}

}

Компонент WeatherWorldClock изолирован от изменений в main.qml. Мы можем дополнять и изменять его свойства в любом файле и не беспокоиться о том, что что-то пойдёт не так. Если WeatherWorldClock станет слишком сложным для поддержки, его можно разделить на большее число компонентов. Именно поэтому очень важным является то, что основная логика нашего приложения выглядит сейчас крайне просто: мы инициализируем компоненты WeatherWorldClock и указываем город и его UTC-смещение. Вот и всё!

Заключение



Данная статья показала, как можно поддерживать чистоту и логичность кода развивающегося и усложняющегося примера при помощи компонентов и применения всем известных принципов объектно-ориентированного проектирования. На один момент — когда мы добавляли возможность просмотра погоды к нашему примеру — мы перестали следить за нашими компонентами, поэтому логика приложения существенно усложнилась. Таким нехитрым способом было продемонстрировано, что поддержание чистоты и порядка в QML-коде является серьёзной работой и требует определённой дисциплинированности от разработчика.

  • qt quick

  • , qml

  • , qt

  • , программирование



+15

  9336



48











Автор: @nokiaman



Microsoft Lumia

рейтинг 63,76

Похожие публикации


Вебинары по QML и QtQuick: Qt Mobility и работа с сенсорами 

Вебинары по QML и QtQuick: интеграция QML и C++ 

Вебинары по QML и QtQuick: пользовательские элементы и использование ресурсов (1)

Лучшие приёмы Qt Quick: связывание свойств в QML (9)

Qt Quick: лучшие приёмы (6)

скачати

© Усі права захищені
написати до нас