Удаленное управление. QML.

  • Автор
И всем снова привет.

Тут вспомнил еще про один проект, который построен на базе ferro_remote клиента.
Это QML клиент. Тем, кто не знает, что это такое можно прочитать тут + много русскоязычных источников. QML это простой язык описания визуального интерфейса со встроенным JS. Была когда-то идея купить себе Jolla, интерфейс, которой построен именно на QML.

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

Вот про этот клиент я и напишу сейчас.

gui_client, а именно так называется этот клиент (с названиями у меня туго, да) представляет собой обычный бинарник, который принимает в командную строку путь к QML файлу, экспортирует в машину некоторые интерфейсы клиента и исполняет файл. Все просто. Все очень похоже на работу lua_client, который я тут описывал не так давно. Есть, однако, пара моментов.

  1. Клиентов в одном экземпляре QML может быть несколько, каждый может быть соединен со своим агентом

  2. Соединяться нужно из скрипта, то есть вызывать методы клиента и реагировать на его события (сигналы)


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

И так. Запуск приложения выглядит вот так
$ ./gui_client test.qml

где test.qml это как раз само окно приложения со всем, что необходимо.
Самый простой пример:

import QtQuick 2.0

Rectangle {
id: mainWindow
}

Теперь, если это запустить, мы получим просто белое окошко, которое можно свернуть, развернуть, или закрыть
Но нужно ж соединиться с малиной, например. Добавим.
import QtQuick 2.0

import Fr.Client 1.0 /// импорт клиента и некоторых интерфейсов

Rectangle {
id: mainWindow
FrClient { // клиент
id: generalClient
Component.onCompleted: connect( "192.168.3.1:12345" ) // соединимся с малиной
}
}

Все. Вызов: (Далее просто можно закрыть)
$ ./gui_client test.qml

В логах агента на стороне малины будет:
2000-Jan-11 02:38:19.472011 [INF] [listener] New connection: ep: tcp://0.0.0.0:12345 client: tcp://192.168.3.10:52849 total: 1
2000-Jan-11 02:38:26.854824 [INF] [listener] tcp://0.0.0.0:12345 Close connection: tcp://192.168.3.10:52849; count: 0

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

Можно сделать что-то поинтереснее. Например валяется у меня Grove LCD RGB.
Как известно из даташита на это устройство, устройства в этой железке 2. Первое отвечает за текст, второе за цвет экранчика. Оба рулятся по i2c. Все должно быть просто.
Кстати сам экранчик выглядит вот так
Это он подключен не к малинке, если что. Просто такой же девайсик с линуксом на борту и агентом ferro_remote.

Для того, чтоб уметь работать с устройством, добавлю 2 компонента FrClientI2C:

Rectangle { // это будет общий компонент-девайс, который мы сможет потом,
// например, вынести в отдельный файл
id: lcdDevice
width: 0
height: 0
FrClientI2c {
id: txtDev // этот для текста
client: generalClient // использовать клиента с id generalClient
busId: 1 // шина 1
slaveAddress: 0x3E // адрес
}

FrClientI2c {
id: rgbDev // этот для цвета
client: generalClient
busId: 1
slaveAddress: 0x62
}
}

Что тут происходит:
Компоненты будут готовы к работе, как только клиент будет соединен с агентом. У каждого компонента (если я нигде не забыл это воткнуть), есть свойство ready и соответственно сигнал "onReadyChanged", в которой передается значение ready (true or false).
Поэтому прям в компонент lcdDevice можно добавить свойство, которое принесет с собой и сигналы, на которые можно реагировать:

Rectangle {
id: lcdDevice
width: 0
height: 0
property bool ready: txtDev.ready && rgbDev.ready // привяжем состояние 2 устройств к 1 значению
........
onReadyChanged: { // устройства изменили состояния
if( ready ) {
..... // оба устройства готовы.
}
}
}

Добавим в код пару функций. Первая будет менять цвет, вторая очищать экран.

Rectangle {
id: lcdDevice
......
function set_color( r, g, b )
{
// установка цвета. за это отвечают 3 регистра.
rgbDev.writeBytes( { 0x04: r, 0x03: g, 0x02: b } )
}
function clear( )
{
// очистка экрана - установка регистра 0x80 в 1
txtDev.writeBytes( { 0x80: 0x01 } )
}
}

и теперь можно при готовности устройств очистить их и сбросить цвет на 0

onReadyChanged: {
if( ready ) {
clear( )
set_color( 0, 0, 0 )
}
}

Так при подключении программка будет убирать текст с экрана и устанавливать цвет в 0x000000 то есть он будет выключен.
Добавлю функцию для установки текста. Я сделаю функцию с 2 параметрами, так как строк у экранчика 2.

/// собственно функция формирует массив из объектов, которые выглядят так: {registry: value}
/// после этого все это пишется в устройство.
/// тут, при использовании массива будет гарантия того,
/// что значение регистров будет установлено ровно в той последовательности,
/// в какой они поступят в массив.
/// 0x40 - регистр, который пишет свое значение в следующее знакоместо.
function set_text( txt, txt2 )
{
/// сначала очистка экрана и установка курсора с положение 1:1
txtDev.writeBytes( [{ 0x80: 0x01 },
{ 0x80: 0x08 | 0x4 },
{ 0x80: 0x28 }] )
var txt_value = [] /// значение регистров для записи текста

for( var i = 0; i < txt.length; i++ ) {
txt_value = txt_value.concat( [{ 0x40: txt.charCodeAt(i) }] )
}
txt_value = txt_value.concat({ 0x80: 0xC0 }) /// переход на 2 строку.
for( i = 0; i < txt2.length; i++ ) {
txt_value = txt_value.concat( [{ 0x40: txt2.charCodeAt(i) }] )
}
txtDev.writeBytes(txt_value) /// Пишем.
}

Если все правильно, то после вызова функции мы увидим на экранчике текст, переданный в параметрах.

onReadyChanged: {
if( ready ) {
//clear( )
set_color( 100, 100, 100 )
set_text( "Hola,", "raspberrypi.ru" )
}
}

Запустив и подключив, мы увидим светлый экран и надпись на нем "Hola,\nraspberrypi.ru"
Дальше - больше. =)

Я добавил ColorDialog из "QtQuick.Dialogs 1.0" и повесил событие смены текущего цвета на смену цвета экрана.

Скрипт можно взять тут. Скрипты немного корявенькие, потому как это вообще первый мой проект, как с QML, так и с Qt вообще.

PS: есть еще примеры работы с удаленным исполнением консольной команды, с листингом директории.

+ Есть работа с пинами GPIO и, недавно начал прикручивать, SPI.

Так же при помощи QML можно легко и непринужденно смотреть видео с железки

Rectangle {
width: 600
height: 200
Video {
anchors.fill: parent
source: "rtsp://192.168.3.1:554/video"
enabled: true
visible: true
focus: true
autoPlay: true
}

}


Пока все. Спасибо за внимание =)

Комментарии:

Для написания комментариев необходимо зарегистрироваться