Данное руководство описывает основные шаги, необходимые для разработки современных графических интерфейсов с использованием библиотеки facefull (HTML/JavaScript) для нативных приложений под ЗОСРВ "Нейтрино"
В этой главе:
С помощью библиотеки facefull можно создавать современные графические пользовательские интерфейсы с использованием технологий HTML, CSS и JS как для веб, так и для нативных приложений. Библиотека содержит более 30 различных визуальных компонентов с огромными возможностями кастомизации. Все компоненты адаптивные и отлично подходят для использования с разными разрешениями экрана, а также с тачскринами.
Библиотека обладает исчерпывающей документацией, а ее исходный код доступен в публичном репозитории.
Для нативных приложений, в качестве рендера интерфейса выступает системный веб-движок, в случае Нейтрино - это WebKit. В Нейтрино имеется поддержка Qt5, поэтому самый простой способ отображения такого интерфейса - использование компонента QWebView
.
Отличие использования facefull для разработки интерфейсов для нативных приложений от классических случаев применения web-based UI в том, что в данном случае в WebView переносится только графический интерфейс (и логика его работы), а вся основная логика приложения остаётся написанной на нативных языках С\С++. Это означает, что приложение не теряет в функциональных возможностях и скорости работы.
Для того, чтобы связать нативный код на С\С++ и код facefull требуется реализация специального механизма обмена сообщениями, который называется bridge. Для этих целей была разработана библиотека facefull-bridge, которая реализует этот механизм для различных фреймворков, в том числе и Qt5WebKit
. Она включает в себя все компоненты библиотеки facefull. Библиотека facefull-bridge была портирована под ОС Нейтрино и доступна "из коробки". Её исходный код также доступен в публичном репозитории.
Таким образом, применение facefull для построения графических интерфейсов в нативных приложениях сводится к трём простым шагам.
Обычно главное окно в Qt создаётся с помощью наследования от класса QMainWindow
. На главном окне необходимо разместить только виджет QWebView и ряд вспомогательный компонентов. Типовой вариант заголовочного файла mainwindow.h
выглядит так:
#ifndef MAINWINDOW_H#define MAINWINDOW_H#include <QMainWindow>#include <QVBoxLayout>QT_BEGIN_NAMESPACEnamespace Ui { class MainWindow; }QT_END_NAMESPACEclass MainWindow : public QMainWindow {Q_OBJECTprivate:Ui::MainWindow *ui;QVBoxLayout *MainLayout;QWidget *MainWidget;QWebView *WebView;public:MainWindow( QWidget *parent = nullptr );~MainWindow();};#endif // MAINWINDOW_H
Реализация класса в файле mainwindow.cpp
содержит создание объекта класса QWebView
и размещение виджета на главном окне:
#include <iostream>#include <QDir>#include "mainwindow.h"#include "ui_mainwindow.h"MainWindow::MainWindow( QWidget *parent ) : QMainWindow( parent ), ui( new Ui::MainWindow ) {ui -> setupUi( this );// Если нужно убрать системную рамку и заголовок окнаsetWindowFlags( Qt::FramelessWindowHint );std::cout << "WebKit version: " << qWebKitVersion().toStdString() << std::endl;MainLayout = new QVBoxLayout();MainLayout -> addSpacing( 0 );MainLayout -> setContentsMargins( 0, 0, 0, 0 );WebView = new QWebView( this );MainLayout -> addWidget( WebView );MainWidget = new QWidget();MainWidget -> setLayout( MainLayout );setCentralWidget( MainWidget );}MainWindow::~MainWindow() {delete ui;}
Для функционирования bridge необходимо подключить подходящий заголовочный файл с реализацией интерфейса и выполнить создание объекта, передав конструктору нужные параметры. Также потребуются некоторые вспомогательные методы.
В описание класса в файле mainwindow.h
нужно внести следующие изменения:
#include <facefull/bridge/qt5webkit.hpp>class MainWindow : public QMainWindow {Q_OBJECTprivate:...FacefullBridgeQt5WebKit *Bridge;...protected:bool eventFilter( QObject* object, QEvent* event ) override;public slots:void doBridgeEventReceive( const QString& ) const;signals:void BridgeEventHandler( QString, QString );...};
Перегруженный метод eventFilter() необходим для реализации перемещения окна с помощью кастомного заголовка, а методы doBridgeEventReceive() и BridgeEventHandler() - слот и сигнал для обработки событий от QWebView
.
В файле mainwindow.cpp
добавится создание объекта класса FacefullBridgeQt5WebKit
и реализация некоторых из указанных методов:
MainWindow::MainWindow( QWidget *parent ) : QMainWindow( parent ), ui( new Ui::MainWindow ) {...Bridge = new FacefullBridgeQt5WebKit(this, WebView, QUrl("путь к html странице"));...}void MainWindow::doBridgeEventReceive( const QString &data ) const {Bridge -> doEventCatch( data.toStdString() );}bool MainWindow::eventFilter( QObject* object, QEvent* event ) {Bridge -> doMoveWindow( (QMouseEvent*)event );return false;}
Здесь переменная respath содержит путь к странице, реализующей графический интерфейс.
Этот шаг сводится к созданию трёх компонентов: HTML страницы, стилей (CSS), и основного JS-скрипта интерфейса приложения. Традиционно файлы называются window.html
, style.css
и app.js
соответственно. Библиотека facefull в свою очередь предоставляет реализацию функционала визуальных компонентов, реализацию внутренней составляющей bridge и стили.
window.html
содержит описание компонентов на языке разметки HTML5, которые должны быть отображены в приложении, а также подключает все необходимые ресурсы. Простейший пример страницы выглядит следующим образом:
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><title>Facefull test</title><meta http-equiv="X-UA-Compatible" content="IE=edge"/><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"><!-- Подключение необходимых ресурсов --><script src="facefull/facefull.min.js" charset="utf-8"></script><script src="src/app.js" charset="utf-8"></script><link rel="stylesheet" href="facefull/facefull.min.css"><link rel="stylesheet" href="src/style.css"></head><body><!-- Контейнер окна, обязательно должен иметь id="W" --><div id="W" class="Window"><!-- Определение заголовка окна, обязательно должен иметь id="WH" --><div id="WH" class="WindowHeader" onselectstart="return false"><div class="WindowIcon"></div><div class="WindowCaption">Facefull app example</div><div class="WindowMover"></div><div class="WindowControlsBlock"><div class="WindowControl Min"><div></div></div><div class="WindowControl Max" id="WCM"><div></div></div><div class="WindowControl Close"><div></div></div></div></div><!-- Контейнер рабочей зоны окна, обязательно должен иметь id="G" --><div id="G" class="GlobalArea"><!-- Определение главного бокового меню --><div class="MainMenu"><div id="MMI" class="MainMenuItems"><div class="TooltipTarget" data-pagename="Page1" data-tooltip-text="Page 1" data-tooltip-width="130" data-tooltip-pos="right"></div><div class="TooltipTarget" data-pagename="Page2" data-tooltip-text="Page 2" data-tooltip-width="120" data-tooltip-pos="right"></div></div></div><!-- Рабочая область окна --><div class="WorkArea"><!-- Определение первой вкладки. В id указывается желаемое id вкладки с префиксом P (т.е. P<id вкладки>). Оно автоматически связывается с элементами главного меню --><div id="PPage1" class="Page"><div class="Title"><div class="TitleText"><div>Вкладка 1</div><div class="Subtitle">Подзаголовок вкладки 1</div></div></div><div class="Box PageBody Scrolling" data-scrollboxname="P1SB"><div class="Scrolldata"><!----></div></div></div><!-- Определение второй вкладки --><div id="PPage2" class="Page"><div class="Title"><div class="TitleText"><div>Вкладка 2</div><div class="Subtitle">Подзаголовок вкладки 2</div></div></div><div class="Box PageBody Scrolling" data-scrollboxname="P1SB"><div class="Scrolldata"><!----></div></div></div></div></div><!-- Определения дополнительных элементов окна: --><!-- Определение всплывающей подсказки --><div id="TT" class="Tooltip"></div><!-- Определение затеняющего оверлея для всплывающих сообщений --><div id="OV" class="Overlay"></div><!-- Определение стандартного окна всплывающих сообщений --><div id="AE" class="Alert Hidden Rounded"><div class="AlertCaption"></div><div class="AlertText"></div><div class="AlertButtons"><div id="AB-OK" class="Button Rounded">OK</div><div id="AB-Y" class="Button Rounded">Yes</div><div id="AB-N" class="Button Rounded">No</div></div></div></div></body></html>
Стандартный файл стилей facefull.min.css
содержит все необходимые стили для стандартных визуальных компонентов библиотеки facefull, но их можно переопределить в собственном CSS файле. Например, можно задать значки пунктам главного меню и значок в заголовке окна:
.MainMenu *[data-pagename="Page1"]::before {content: '\F056E';}.MainMenu *[data-pagename="Page2"]::before {content: '\F0D7C';}.WindowIcon {font-family: "Material Design Icons";font-size: 28px;}.WindowIcon::before {content: '\F126F';}
В состав библиотеки facefull входит шрифт Material Design Icons, содержащий сотни значков в минималистичном стиле.
Теперь необходимо описать скрипт инициализации графического интерфейса. JS файл (app.js
) должен содержать следующие обязательные определения:
// Инициализация объекта facefull. Все взаимодействия с библиотекой осуществляются через этот объектfacefullCreate( true );// Запуск инициализации интерфейса после загрузки страницыwindow.addEventListener( 'load', function ) {App();});function App() {// Инициализация компонентов facefullfacefull.doInit();// ...// Иницализация графического интерфейса всегда должна заканчиваться отправкой в bridge сообщения doWindowReady. Это сообщение генерирует событие, которое означает, что интерфейс проинициализирован и готов к работе. После этого можно отправлять и получать сообщения через bridge.facefull.doEventSend( "doWindowReady" );}
Все ресурсы графического интерфейса можно использовать как напрямую с файловой системы, так и через QRC. QRC позволяет "вкомпилировать" их в бинарный файл, что удобнее при распространении приложения. Пример файла описания ресурсов выглядит следующим образом:
<!DOCTYPE RCC><RCC version="1.0"><qresource prefix="/"><file>ui/window.html</file><file>ui/app.js</file><file>ui/style.css</file><file>ui/facefull/facefull.min.js</file><file>ui/facefull/facefull.min.css</file><file>ui/facefull/theme-light.min.css</file><file>ui/facefull/fonts/md-embedded.woff</file></qresource></RCC>
Таким образом, все ресурсы будут доступны из основного кода приложения через префикс "qrc:". Теперь нужно указать в конструкторе при создании bridge правильный путь к html странице графического интерфейса:
Bridge = new FacefullBridgeQt5WebKit( this, WebView, QUrl( "qrc:/ui/window.html" ) );
Теперь после запуска приложения можно увидеть получившийся результат:
Со стороны нативного кода отправка сообщений через bridge осуществляется с помощью метода doEventSend(), для приёма сообщений необходимо создать обработчик события с помощью метода doEventAttach(). Аналогичным образом взаимодействие осуществляется и со стороны UI - отправка и приём выполняется с помощью методов facefull.doEventSend() и facefull.doEventHandlerAttach() соответственно.
Рассмотрим пример. Чтобы отправить тестовое сообщение в UI в нативном коде (например в конструкторе класса MainWindow
) выполняем соответствующий вызов:
// Навешиваем обработчик на событие готовности окна и отправляем сообщениеBridge -> doEventAttach( "doWindowReady", [this]( const std::string& data ) ) {Bridge -> doEventSend( "doTestMessage", "Тестовое сообщение" );});
В свою очередь в app.js
добавляем обработчик события:
facefull.doEventHandlerAttach( "doTestMessage", function( data ) ) {AlertShow("Сообщение", data, "info", "OK");});
Теперь при запуске приложения будет появляться всплывающее сообщение с текстом "Тестовое сообщение":
Библиотека facefull-bridge (библиотека визуальных компонентов facefull входит в её состав) является open-source проектом, который был портирован и адаптирован под использование в ОС Нейтрино. Описанный в статье подход позволяет с минимальными усилиями начать создавать современные графические интерфейсы, например, для вывода таблиц, графиков или других важных показателей ФПО. При этом сохраняется производительность и функциональность этого ФПО, так как нативный код приложения остаётся нативным.
Полный исходный код описанного в статье примера использования библиотеки facefull-bridge доступен в публичном репозитории СВД ВС.
Список поддерживаемых визуальных компонентов, доступных "из коробки", постоянно расширяется. Сейчас доступны различные кнопки, переключатели, списки, графики, поля ввода, меню и много другое. Использование библиотеки не требует специальных знаний (только чтение документации на API), так как применяются стандартные HTML5, CSS и JS. Полученный графический интерфейс приложения легко переносится из нативного режима в браузер, если потребуется создание web-приложения на его основе.
Предыдущий раздел: перейти