Интеграция приложения на QML с веб-ресурсами

  • Tutorial
Здравствуй, дорогой хабражитель! Я хочу рассказать, как интегрировать программу на новомодном языке QML с веб-ресурсами.

Сам по себе, QML — это декларативный JavaScript-подобный язык программирования, который входит в фреймворк Qt. Разработчики Qt настроены серьезно и продвигают его как основной инструмент создания интерфейсов. Более того, достаточно много вещей можно сделать не прибегая вообще к C++, в том числе и возможность работы с веб-серверами.

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

Учитывая, что в Qt 5.1, альфа версия которой вышла на прошлой неделе, включена начальная поддержка Android и iOS, эта тема может быть особенно интересна тем, кто присматривается к Qt или активно ее осваивает. В этой статье я расскажу, как можно организовать работу с веб-ресурсами из приложения на QML на примере API ВКонтакте.

На всякий случай отмечу, что я рассматриваю последнюю стабильную версию Qt 5.0.2. В более ранних версиях каких-то возможностей может не быть.

Что такое XMLHttpRequest и зачем он нужен

Наверняка, многие из читателей слышали про такую технологию, как AJAX (Asynchronous JavaScript And XML). Она позволяет отправлять асинхронные запросы на сервер и обновлять содержимое страницы без ее перезагрузки. В современных браузерах есть различные средства для этого, XMLHttpRequest является одним из них. Поскольку QML является JavaScript-подобным языком и JavaScript окружение в нем похоже на браузерное, то и XMLHttpRequest тоже присутствует. Далее в тексте я буду также записывать его название в сокращенной форме — XHR.

Собственно, что это такое и что оно нам дает? Это инструмент для асинхронных (в браузерах поддерживаются также и синхронные) HTTP-запросов. Несмотря на свое название, он позволяет передавать данные не только в формате XML, хотя изначально был предназначен именно для этого. Реализация в движке QML поддерживает следующие HTTP-запросы: GET, POST, HEAD, PUT и DELETE. В основном, мы будем пользоваться первыми двумя.

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

Процедура работы с XMLHttpRequest

Процесс работы с XHR выглядит следующим образом.

1. Создаем объект XHR:

var request = new XMLHttpRequest()


2. Инициализируем объект, указывая тип запроса (он же HTTP-метод), адрес и, если нужно, параметры запроса [1], которые нужно передать серверу:

request.open('GET', 'http://site.com?param1=value1&param2=value2')


Первым параметром передаем тип запроса, вторым — URL. Для GET-запроса параметры нужно передать здесь же, отделив их от адреса символом '?'. Параметры разделяются символом '&'.

Для POST-запроса нужно указать тип содержимого. Если мы передаем данные параметрами запроса, то делается это следующим образом:

request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')


3. Устанавливаем обработчик на смену состояния запроса. В большинстве случаев, нам надо просто дождаться, пока запрос не завершится и затем выполнить обработку результата либо ошибок. При завершении запроса параметр readyState будет равен XMLHttpRequest.DONE (подробнее про значения см. [2]).

request.onreadystatechange = function () {
    if (request.readyState === XMLHttpRequest.DONE) {
        if (request.status === 200) {
            console.log(request.responseText)
        } else {
            console.log("HTTP request failed", request.status)
        }
    }
}


Наша анонимная функция будет вызывать при каждом изменении свойства readyState. Нас интересует завершение запроса, после которого мы проверяем, успешно ли он выполнился. Для этого мы сверяем код его статус с кодом успешного завершения (200). HTTP является текстовым протоколом и помимо числовых значений кодов, передается еще и текстовое описание, так что можно сравнивать свойство statusText со строкой, соответствующей этому статусу, в данном случае, это строка «OK»:

if (request.statusText === 'OK')


В случае ошибки, status и statusText будут содержать код и текстовое описание кодов состояния HTTP (например, 404 и «Not Found» соответственно).

4. Отправляем запрос.

request.send()


В случае POST, здесь же нужно передать параметры запроса:

request.send('param1=value1&param2=value2')


В параметрах запроса можно передавать далеко не все символы. Поэтому и параметр и значение стоит кодировать и, если надо, соответственно декодировать специальными функциями — encodeURIComponent() и decodeURIComponent(). Пример использования:

request.send('%1=%2'.arg(encodeURIComponent(param)).arg(encodeURIComponent(value)))


Рекомендуется закодированную строку еще дополнительно обработать и заменить последовательность "%20" (т.е. закодированный пробел) на символ '+'. Перед декодированием, соответственно, сделать наоборот.

Обычно параметрами запроса передаются значения простых типов. Можно передать и массив, но синтаксис несколько мутный. Например, отправка массива params из двух значений будет выглядеть так:

request.send('params[]=value1&params[]=value2')


Если изловчиться, то можно в качестве значений передавать даже объекты (!), но это может быть не совсем надежно, в том плане, что на принимающей стороне он может превратится в массив :)

Используя POST-запросы мы можем передавать данные не только параметрами запроса но и в самом теле запроса. Например, можно отправить данные в формате JSON. Для этого нужно установить правильный Content-Type и размер содержимого (Content-Length). Пример отправки такого запроса:

request.setRequestHeader('Content-Type', 'application/json')
var params = {
    param1: value1,
    param2: value2
}
var data = JSON.stringify(params)
request.setRequestHeader('Content-Length', data.length)
request.send(data)


Здесь JSON — это глобальный объект доступный в QML, предоставляющий средства по работе с данным форматом [3].

Фактически, формат, в котором мы можем передавать данные, определяется сервером. Если он принимает JSON — отлично, шлем JSON. Ожидает, что данные придут параметрами запроса — значит так и надо отправлять.

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

Получение и отображение списка друзей

Для начала рассмотрим простой пример с методами, не требующими авторизации и других лишних телодвижений. Получение списка друзей попадает в эту категорию. Напишем простую программу, при старте отправляющую XHR на получение списка друзей и после его получения отображающую имена пользователей и их аватарки.

Большую часть кода составляет интерфейс отображения и его особо описывать нет смысла. Отмечу только, что если в качестве модели используется JavaScript объект или массив, то для получения к данным модели используется modelData вместо model.

Наиболее интересная часть здесь — это работа с сервером. Для доступа к API ВКонтакте есть специальный адрес: api.vk.com/method. К полученному адресу мы добавляем название метода (список методов можно посмотреть в [4]), в нашем случае это метод friends.get. На этот адрес нужно отправить POST или GET-запрос с необходимыми параметрами. Ответ придет в формате JSON. Нам нужно в параметре uid передать ID пользователя. Также в параметре fields передадим еще photo_medium, чтобы получить фото и чтобы оно было не самого маленького размера.

Ниже собственно исходный текст программы. В качестве userId в main задается ID пользователя.

import QtQuick 2.0

Rectangle {
    id: main

    property int userId: XXX
    property var friends

    width: 320
    height: 640
    color: 'skyblue'

    function getFriends() {
        var request = new XMLHttpRequest()
        request.open('POST', 'https://api.vk.com/method/friends.get')
        request.onreadystatechange = function() {
            if (request.readyState === XMLHttpRequest.DONE) {
                if (request.status && request.status === 200) {
                    console.log("response", request.responseText)
                    var result = JSON.parse(request.responseText)
                    main.friends = result.response
                } else {
                    console.log("HTTP:", request.status, request.statusText)
                }
            }
        }
        request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
        request.send('fields=photo_medium&uid=%1'.arg(main.userId))
    }

    ListView {
        id: view

        anchors.margins: 10
        anchors.fill: parent
        model: friends
        spacing: 10

        delegate: Rectangle {
            width: view.width
            height: 100
            anchors.horizontalCenter: parent.horizontalCenter
            color: 'white'
            border {
                color: 'lightgray'
                width: 2
            }
            radius: 10

            Row {
                anchors.margins: 10
                anchors.fill: parent
                spacing: 10

                Image {
                    id: image

                    height: parent.height
                    fillMode: Image.PreserveAspectFit
                    source: modelData['photo_medium']
                }

                Text {
                    width: parent.width - image.width - parent.spacing
                    anchors.verticalCenter: parent.verticalCenter
                    elide: Text.ElideRight
                    renderType: Text.NativeRendering
                    text: "%1 %2".arg(modelData['first_name']).arg(modelData['last_name'])
                }
            }
        }
    }

    Component.onCompleted: {
        getFriends()
    }
}


Я сделал вывод в консоль того, что придет в ответе, это удобно, если возникнет желание поиграться с этим примером.

Запустив программу, если был указан действительный ID, мы получим примерно такую картину:



Самая большая сложность здесь именно в работе с XHR. Попробуем разобраться с этим и немного упростить код.

Упрощение работы с XMLHttpRequest

В работе с XHR есть две сложности.

1. При передаче данных параметрами запроса, этот запрос нужно составлять. В случае, если эти параметры могут меняться, то скорее всего в коде будет много операций, склеивающие параметры запроса из кусочков. К тому же, нужно не забывать про то, что неплохо бы еще и при составлении ключи и значения кодировать при помощи encodeURIComponent, как я уже писал выше. Итого код, формирующий эти параметры может получиться громоздким и не очень понятным. Гораздо удобнее было бы в качестве параметров использовать объект, в котором установлены соответствующие поля.

Я написал небольшую библиотеку на JavaScript, которая преобразует объект в параметры запроса, все кодирует, в общем, выдает готовую строку, которую можно сразу отправлять. Также есть функция, которая декодирует параметры запроса и создает из них объект (но она поддерживает только простые типы, массив или объект в параметрах не распарсит, впрочем, вряд ли это понадобится). Взять можно здесь: github.com/krnekit/qml-utils/blob/master/qml/URLQuery.js.

2. В зависимости от типа запроса, данные отправлять нужно по-разному, да еще и может понадобиться устанавливать дополнительно заголовки. Я написал библиотеку, которая упрощает отправку XHR, предоставляя единый интерфейс. Она может отправлять данные в любом формате, для этого можно передать параметром тип содержимого, по умолчанию считается все тот же «application/x-www-form-urlencoded», при этом стоит помнить, что данные другого типа при помощи GET-запроса передавать нельзя, в таком случае нужно будет использовать POST. Content-Length так же автоматически посчитается и установится. Принимает типа запроса, URL, функцию-callback (опционально), которая будет вызвана при завершении запроса и тип данных (опционально). Функция возвращает сам объект запроса или null в случае ошибки. Взять можно здесь: github.com/krnekit/qml-utils/blob/master/qml/XHR.js

Используя две данные библиотеки я упростил предыдущий пример. Весь код здесь приводить не буду, рассмотрим только то, что изменилось.
В начале файла мы подключаем библиотеки (в данном примере, файлы библиотек лежат в том же каталоге, что и qml-файл):

import 'URLQuery.js' as URLQuery
import 'XHR.js' as XHR


Мы импортируем библиотеки и задаем для них пространства имен (namespace), через которые мы будем обращаться к функциям из библиотек.

Функция, отправляющая XHR выглядит теперь так:

function getFriends() {
    var params = {
        fields: 'photo_medium',
        uid: main.userId
    }

    function callback(request) {
        if (request.status && request.status === 200) {
            console.log("response", request.responseText)
            var result = JSON.parse(request.responseText)
            main.friends = result.response
        } else {
            console.log("HTTP:", request.status, request.statusText)
        }
    }

    XHR.sendXHR('POST', 'https://api.vk.com/method/friends.get', callback, URLQuery.serializeParams(params))
}


Для начала мы определяем объект с параметрами запроса. Затем функцию-callback, которая вызовется при завершении запроса. Функция получает параметром сам запрос. И затем отправляем сам запрос, преобразовав объект с параметрами при помощи функции serializeParams.
В итоге, размер кода, можно сказать, не изменился, но зато он стал гораздо более структурированным и понятным.

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

Авторизация ВКонтакте из QML

Не все методы работают без авторизации, так что скорее всего, нам нужно будет авторизоваться. В результате мы должны получить т.н. Authorization Token, который затем будем передавать в запросах к ВКонтакте. Для того, чтобы мы могли авторизоваться, нужно создать в ВКонтакте приложение. Сделать это можно здесь: vk.com/editapp?act=create. Выбираем тип Standalone-приложение. Затем его ID мы будем передавать одним из параметров запроса.

1. Способы авторизации

Поскольку мы делаем standalone-приложение, то есть два способа авторизации, у обоих есть свои проблемы, так что нужно выбрать наименьшее зло :)

1. Прямая авторизация. Посылается HTTP-запрос с данными для логина на определенный адрес. В ответ придут данные в формате JSON, содержащие токен или описание ошибки.

Преимущества:
  • Простота.

Недостатки:
  • Нужно передавать секретный код приложения (возможно даже придется зашить в программу), соответственно есть риск его утечки.
  • Такой способ будет работать только для доверенных приложений. При создании нового приложения он будет недоступен и чтобы его включили нужно писать в поддержку.


2. Авторизация OAuth. Реализуется следующим образом. В программу нужно встроить браузер, в котором пользователю показать специальную страницу логина. После авторизации произойдет перенаправление на другую страницу и в текущем URL будет находиться токен либо описание ошибки. ВКонтакте этот способ позиционирует как основной.

Преимущества:
  • Главное и очень существенное преимущество в том, что он работает для всех приложений и для приложений, которым не разрешили прямую авторизацию, это вообще единственный способ.
  • Не надо передавать секретный ключ.
  • OAuth является стандартом и точно также можно авторизоваться в Facebook, например.


Недостатки, впрочем, тоже существенные.

  • Нужно открывать страницу ВКонтакте, а значит либо пытаться встроить ее в окно программы либо открывать в отдельном окне.
  • Поскольку мы открываем страницу, то нам нужен и браузер. Соответственно, придется тащить QtWebkit и все, что он за собой потянет, отчего программа прибавит в весе.
  • Нужно будет перехватывать события смены URL встроенного браузера, парсить этот URL и выбирать из него параметры, что несколько сложнее, чем XHR.


2. Прямая авторизация

Я, конечно, запросил чтобы мне включили возможность прямой авторизации, но поддержка ВКонтакте сначала неторопливо расспрашивала меня, что да зачем мне надо, а потом полный доступ все-таки зажала :( Так что рассмотрим чисто теоретически. Выглядеть это будет примерно так:

function login() {
    var params = {
        grant_type: 'password',
        client_id: 123456,
        client_secret: 'XXX',
        username: 'XXX',
        password: 'XXX',
        scope: 'audio'
    }

    function callback(request) {
        if (request.status && request.status === 200) {
            console.log("response", request.responseText)
            var result = JSON.parse(request.responseText)
            if (result.error) {
                console.log("Error:", result.error, result.error_description)
            } else {
                main.authToken = result.auth_token
                // Now do requests with this token
            }
        } else {
            console.log("HTTP:", request.status, request.statusText)
        }
    }

    XHR.sendXHR('POST', 'https://oauth.vk.com/token', callback, URLQuery.serializeParams(params))
}


В начале формируем параметры, в них я для примера указал, что требуется доступ к аудио записям пользователя (параметр scope). Затем функция-callback, которая в случае ошибки пишет в консоль, а в случае успеха сохраняет токен и дальше уже могут идти запросы к API.

На всякий случай, оставлю ссылку на документацию: vk.com/dev/auth_direct.

3. Авторизация через OAuth.

Для этого типа авторизации нам нужно показать пользователю веб-страницу логина. В QtQuick есть компонент WebView, позволяющий встроить в приложение на QML браузер на движке WebKit. После того, как пользователь авторизуется, URL в браузере сменится и, в случае удачной авторизации будет содержать токен в параметрах запроса или описание ошибки в якоре [5].

Чтобы не морочиться с разбиранием этого URL, используем функцию parseParams из URLQuery. Ей можно передать сразу весь URL, на выходе мы получим объект с параметрами.

Ниже описан компонент, реализующий этот функционал.

LoginWindow.qml:
import QtQuick 2.0
import QtQuick.Window 2.0
import QtWebKit 3.0
import "URLQuery.js" as URLQuery

Window {
    id: loginWindow

    property string applicationId
    property string permissions
    property var finishRegExp: /^https:\/\/oauth.vk.com\/blank.html/

    signal succeeded(string token)
    signal failed(string error)

    function login() {
        var params = {
            client_id: applicationId,
            display: 'popup',
            response_type: 'token',
            redirect_uri: 'http://oauth.vk.com/blank.html'
        }
        if (permissions) {
            params['scope'] = permissions
        }

        webView.url = "https://oauth.vk.com/authorize?%1".arg(URLQuery.serializeParams(params))
    }

    width: 1024
    height: 768

    WebView {
        id: webView

        anchors.fill: parent

        onLoadingChanged: {
            console.log(loadRequest.url.toString())

            if (loadRequest.status === WebView.LoadFailedStatus) {
                loginWindow.failed("Loading error:", loadRequest.errorDomain, loadRequest.errorCode, loadRequest.errorString)
                return
            } else if (loadRequest.status === WebView.LoadStartedStatus) {
                return
            }

            if (!finishRegExp.test(loadRequest.url.toString())) {
                return
            }

            var result = URLQuery.parseParams(loadRequest.url.toString())
            if (!result) {
                loginWindow.failed("Wrong responce from server", loadRequest.url.toString())
                return
            }
            if (result.error) {
                loginWindow.failed("Error", result.error, result.error_description)
                return
            }
            if (!result.access_token) {
                loginWindow.failed("Access token absent", loadRequest.url.toString())
                return
            }

            succeeded(result.access_token)
            return
        }
    }
}


Мы отображаем этот компонент в отдельном окне. После вызова метода login(), будет загружена страница логина.



После авторизации будет совершен переход на URL в котором в качестве адреса будет oauth.vk.com/blank.html, а затем через '?' или '#' будет идти результат. Параметром permissions мы задаем необходимые нам права доступа. Если мы там что-то указываем, то при логине через наш виджет пользователь увидит диалог предоставления прав доступа приложению.

Для того, чтобы понять, когда мы перешли на нужный адрес, мы устанавливаем обработчик onLoadingChanged. Он принимает объект loadRequest, из которого мы получаем всю нужную нам информацию. Он вызывается несколько раз и нас интересует ситуация либо когда произошла ошибка, в случае чего мы посылаем соответствующий сигнал, либо когда нужная страница загрузилась. В этом случае мы проверяем, пришел ли нам токен и, если да, посылаем сигнал об успешной авторизации, иначе сигнал об ошибке.

Ну а теперь рассмотрим саму программу, которая этот виджет использует. Программа в случае успешной авторизации устанавливает статус пользователя в «test». ID пользователя задается свойством userId в main.

import QtQuick 2.0
import 'URLQuery.js' as URLQuery
import 'XHR.js' as XHR

Rectangle {
    id: main

    property int userId: XXX
    property var authToken

    width: 640
    height: 320

    function processLoginSuccess(token) {
        loginWindow.visible = false
        authToken = token

        setStatus()
    }

    function setStatus() {
        var params = {
            access_token: main.authToken,
            text: 'test'
        }

        function callback(request) {
            if (request.status == 200) {
                console.log('result', request.responseText)
                var result = JSON.parse(request.responseText)
                if (result.error) {
                    console.log('Error:', result.error.error_code,result.error.error_msg)
                } else {
                    console.log('Success')
                }
            } else {
                console.log('HTTP:', request.status, request.statusText)
            }

            Qt.quit()
        }

        XHR.sendXHR('POST', 'https://api.vk.com/method/status.set', callback, URLQuery.serializeParams(params))
    }

    LoginWindow {
        id: loginWindow

        applicationId: XXX
        permissions: 'status'
        visible: false
        onSucceeded: processLoginSuccess(token)
        onFailed: {
            console.log('Login failed', error)
            Qt.quit()
        }
    }

    Component.onCompleted: {
        loginWindow.visible = true
        loginWindow.login()
    }
}


После загрузки нам покажется окно логина. После логина оно скрывается и отправляется запрос на сервер для смены статуса пользователя. После этого, программа пишет в консоль результат и завершается.

После того, как мы авторизовались, нам не надо больше запрашивать токен, если нам не понадобились какие-то дополнительные права доступа или его время жизни не истекло (нам его возвращают вместе с токеном, в случае успешной авторизации).

Для чего еще можно применять XMLHttpRequest

Расскажу небольшую историю из своего опыта, не связанную с ВКонтакте, но зато связанную с XHR.

Как-то у моего коллеги возникла задача получать и обрабатывать в QML данные в формате XML.

В QtQuick есть специальный тип XmlListModel, способный вытащить из сети, распарсить и представить в виде модели XML-файл. Ему нужно задать запрос типа XPath, в соответствии с которым будет наполняться модель. Проблема была в том, что XML-файл содержал не только элементы, которые нужно было выбрать и поместить в модель но и некоторую дополнительную информацию, которую тоже нужно было получить.

Методов решения несколько. Можно использовать два объекта XmlListModel, но это однозначный костыль, к тому же не хотелось, чтобы XML-файл перекачивался два раза (а он будет, проверено). Можно реализовать этот функционал используя Qt, который содержит аж целых несколько вариантов парсеров, но было желание решить задачу на чистом QML.

Поскольку XMLHttpRequest изначально задумывался для работы именно с XML, в нем есть средства для работы с XML. Соответственно, можно получить и распарсить XML его средствами и выбрать нужную информацию. Затем можно этот же XML передать в XmlListModel (туда можно передавать не только URI, но и содержимое XML-файла).

Так что, несмотря на то, что сейчас XMLHttpRequest используется для чего угодно, не стоит забывать, для чего он был создан и что там есть еще и инструменты по работе с XML.

Небольшое резюме

QML содержит много инструментов, доступных для JavaScript в браузере. XMLHttpRequest позволяет отправлять HTTP-запросы и тем самым обеспечивать интеграцию приложения на QML с веб-ресурсами. Использование XHR позволяет во многих случаях обойтись без использования C++ для обмена данными с сервером и тем самым упростить разработку.
Share post
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 14

    0
    Как то с плюсовым бэкендом у меня код покрасивее вышел. Да и вероятнее всего быстрее. лучше без сильной нужды не писать всё на чистом qml, многие вещи только сложнее будут.
      0
      Для тех, кто плюсами владеет — да, несомненно. Для меня же, например, такой вариант более приемлим.
      • UFO just landed and posted this here
          0
          Ну по мне так QML должна выполнять именно свою функцию — GUI.
          Типа вот как устроен радиоприемник. Есть гуи — сама коробка, крутилки там всякие, крутишь — стрелочка частот бегает… и т.п. Ничего не работает только, т.к. коробка пуста. Потом внутрь засовываешь всю электронику, подключаешь все проводками ( сигналы-слоты, модели ) и все заработало. Внутренности можно поменять на другие, как и выкинуть устаревшую коробку (гуи) и поставить новую и красивую. А можно вообще выпускать пять разных коробок с одной и той-же начинкой. ИМХО вообще любое приложение должно иметь возможность работать вообще без gui.
        +2
        Если приложение практически полностью написано на QML, то вариант обойтись без кода на C++ очень заманчивый. Например, для виджета, показывающего погоду и опрашивающего сервер раз в час вполне подойдет.
        0
        Вот спасибо! Как раз запара была вчера на эту тему…
          +1
          На QML очень красиво выглядят приложения в духе «браузер на весь экран» или «пару прямоугольников с двумя строками текста и картинкой». А как там нынче дела с полноценными UI-компонентами (ну там вкладки\таблицы\списки\менюшки)? Получится сделать полноценное приложение? И на всех платформах будет выглядеть нормально?
            +1
            Есть такие компоненты, QtQuick Controls (раньше назывались Qt Desktop Components) В версии 5.1 они будут включены в состав Qt, а сейчас их можно собрать отдельно.
              0
              Меня вот удивляет, что переезд на QML, как на основную платформу для разработки начался с версии 5.0, при том, что в ней по-сути, без этих компонентов ничего существенного не напишешь, а выкатят их только в 5.1, да и то, как всегда, первым релизом пользоваться будет нельзя. Не понимаю таких переходов — на старое уже забили, а новое еще не выкатили.
                +1
                Ну почему же, отдельно они вполне себе собираются и пользоваться можно. Доступны они уже как минимум два года, просто далеко не все и не сразу попадает в основную ветку Qt.
                Да и без них под десктопы вполне можно делать интерфейсы, особенно с кастомным интерфейсом — там все равно придется свое делать. Игрушки, опять же.
                А на мобильных платформах есть свои библиотеки QML компонентов. Это и Cascades в BlackBerry, и Silica у SailfishOS, у Ubuntu и тоже что-то свое есть.
                И на старое никто не забивал, на виджетах все прекрасно пишется.
                  0
                  >и Cascades в BlackBerry, и Silica у SailfishOS, у Ubuntu и тоже что-то свое есть

                  Обожемой! Я почему-то был уверен, что компоненты придут к унификации на мобильных платформах, ан нет — у каждого свой велосипед. Прощай, утопичная идея кросскомпиляции.
                    +1
                    Ну так у каждой платформы свои гайдлайны по интерфейсам, свой стиль и т.д. На десктопах разработчики Qt сделали библиотеку виджетов, которые выглядят нативно под каждой платформой. А на мобильных получается наоборот: разработчики мобильных платформ берут за основу элементы QML и расширяют их в сответствии со своими потребностями. Получается, что база одна, но у каждого свои особенности.
                    И ведь по другому и не получится. Если на десктопах еще есть некий стандартный устоявшийся набор виджетов, то на мобильных устройствах каждый лепит, что хочет. Так что предоставить им базовые компоненты, а они уже пусть расширяют — вполне здравая идея.
            0
            Надеюсь, что в реальной работе вы не прописываете обращения к АПИ в интерфейсе программы (вместе с разметкой).
              0
              QML — это не только разметка :) Кстати, можно этот код вынести в отдельную js-библиотеку.

            Only users with full accounts can post comments. Log in, please.