Как подружить Electron и Webix. Часть 2. Создаем приложение со своим View

—

от автора

Введение

В предыдущей статье мы рассмотрели с Вами вопрос интеграции фреймворка «Webix» в «Electron» и создание на основе этих фреймворков простого GUI приложения. Цель данной статьи является дальнейшую развитие вопроса интеграции в GUI интерфейс, построенный с помощью «Electron» и «Webix» других «JavaScript» фреймворков. Все элементы GUI реализованные в «Webix» характеризуются параметром «view». В зависимости от значения этого параметра будет отображаться тот или иной GUI элемент. Количество типов элементов «view», которые позволяет создавать «Webix» перекрывают порядка 90% задач при реализации интерфейса. Оставшиеся 10% это как раз тот случай, когда необходимо осуществить интеграцию либо ранее написанного кода либо фреймворка (библиотеки), которые в явном виде не поддерживаются в «Webix». Для осуществления интеграции со сторонними фреймворками(библиотеки) создадим вместе с Вами свой «view» элемент «Webix».

Постановка задачи

Создать GUI приложение «Electron+Webix», которое будет строить график функции вида «y=a*sin(b)+c» с возможностью изменения параметров функции «a, b и c» с динамической перерисовкой графика.

Небольшая оговорка

Сразу оговорюсь о том, что в «Webix» есть стандартные очень мощные средства для работы с графиками. Моей целью является не дополнить эти средства, а показать, как можно интегрировать в связку «Electron+Webix» дополнительные фреймворки или библиотеки. Просто так уж сложилось что у меня был готовый код этого графика с использованием «D3.js» и я решил его интегрировать в приложение.

Инструменты

  1. Visual Studio Code – редактор code.visualstudio.com.
  2. Node.JS – скачать вот отсюда nodejs.org/en
  3. Electron – установить «npm install —save-dev electron».
  4. Webix – скачать вот отсюда ru.webix.com.
  5. JQuery – установить «npm jquery»
  6. D3.js – скачать вот отсюда d3js.org
  7. Исходный код (https://github.com/kapakym/electron_webix) приложения, написанного в предыдущей статье.

Подготовка рабочего места

1. Так как я человек ленивый, то второй раз писать весь код с начала не хочу. Поэтому берем код из предыдущей статьи и заливаем его в нужную нам папку. Тут варианта два. Первый это просто скачать архив с сайта (рис. 1) либо его клонировать если у Вас установлен клиент «Git» (рис. 2).


Рис. 1 – Загрузка исходного кода с сайта


Рис. 2 – Клонирование репозитория

2. Что бы в дальнейшем не путаться переименуем папку (это необязательно), например вот так «electron_webix_2» и откроем ее в Visual Studio Code (рис. 3).


Рис. 3 – Открываем проект

3. Установим необходимые элементы. Первое что, нужно сделать это скачать по ссылке, приведенной в инструментах выше «Node.JS» и установить его. Без «Node.JS» у Вас ничего дальше не получиться. Далее открываем консоль «Ctr+`» в Visual Studio Code и вводим поочередно следующие команды (рис. 4):

  • «npm install —save-dev electron»;
  • «npm install jquery».


Рис. 4 – Установка Node.JS и JQuery

4. Далее нам с Вами еще понадобиться фреймворк «D3.js», его также качаем по ссылке из инструментов (файл «d3.zip»). Из загруженного архива «d3.zip» достаем файл «d3.js» и кладем его в папку “libs” (рис. 5).


Рис. 5 – Добавление библиотеки D3.js

На этом рабочее место мы с Вами подготовили.

Верстаем интерфейс

Теперь, когда рабочее место подготовлено перейдем непосредственно к созданию нашего приложения. Оно будет состоять из следующих «view» элементов:

  • — toolbar, стандартный элемент «Webix». Данный элемент будет размещаться в верхней части окна и содержать кнопки управления окном и название приложения. Этот элемент интерфейса у нас с Вами уже был создан в материалах предыдущей стать и тут мы менять ничего не будем;
  • — myview, который нам с Вами предстоит как раз создать, так сказать, кастомный “view”. Он будет представлять из себя область, в которой будет строиться график нашей функции с помощью фреймворка “D3.js”. Данную область кроме всего прочего научим подстраиваться под размеры окна нашего приложения. Разместим его в средней части окна приложения;
  • — slider, стандартный элемент “Webix”. Данный элемент позволяет изменять свое значение с помощью так называемого «ползунка». Таких элементов создадим 3 шт., каждый будет отвечать за изменение определенного параметра нашей функции «a, b или c». Разместим их в нижней части окна приложения.

Приступим. Откроем файл “renderer.js” и отредактируем его т.е. уберем ненужные нам элементы из предыдущей статьи и заменим их элементами описанными выше.

Файл “renderer.js” было вот так:

// Подключаем модуль электрона const { remote } = require('electron') // Получаем указатель на окно браузера где будет производиться отображение нашего интерфейса let WIN = remote.getCurrentWindow() // Подключаем фреймворк webix const webix = require('./libs/webix.min.js') //Подключаем фреймворк JQuery $ = require('jquery') // Функция в которую в качестве параметра как раз и будет передаваться наша с Вами верстка webix.ui(     {         "id": 1587908357897,         "rows": [             {                 "css": "webix_dark",                 "view": "toolbar",                 "height": 0,                 "cols": [                     { "view": "label", "label": "Elcetron +Webix its cool!", css:"head_win" },                     { "label": "-", "view": "button", "height": 38, "width": 40, id:"min-bt" },                     { "label": "+", "view": "button", "height": 38, "width": 40, id:"max-bt" },                     { "label": "x", "view": "button", "height": 38, "width": 40, id:"close-bt" }                 ]             },             {                 "width": 0,                 "height": 0,                 "cols": [                     { "url": "demo->5ea58f0e73f4cf00126e3769", "view": "sidebar", "width": 177 },                     {                         "width": 0,                         "height": 0,                         "rows": [                             { "template": "Hello WORLD! ", "view": "template" },                             {                                 "url": "demo->5ea58f0e73f4cf00126e376d",                                 "type": "bar",                                 "xAxis": "#value#",                                 "yAxis": {},                                 "view": "chart"                             }                         ]                     }                 ]             }         ]     } )  // Закрытие окна $$("close-bt").attachEvent("onItemClick", () => {     const window = remote.getCurrentWindow();     window.close(); })  // Свернуть окно $$("min-bt").attachEvent("onItemClick", () => {     const window = remote.getCurrentWindow();     window.minimize(); })  // Распахнуть окно $$("max-bt").attachEvent("onItemClick", () => {     const window = remote.getCurrentWindow();     if (!window.isMaximized()) {         window.maximize();     } else {         window.unmaximize();     } }) 

Станет вот так:

// Подключаем модуль электрона const { remote } = require('electron') // Получаем указатель на окно браузера где будет производиться отображение нашего интерфейса let WIN = remote.getCurrentWindow() // Подключаем фреймворк webix const webix = require('./libs/webix.min.js') //Подключаем фреймворк JQuery $ = require('jquery') // Функция в которую в качестве параметра как раз и будет передаваться наша с Вами верстка webix.ui(     {         "id": 1587908357897,         "rows": [             {                 // view : toolbar - 1 элемент нашего интерфейса                 "css": "webix_dark",                 "view": "toolbar",                 "height": 0,                 "cols": [                     // Название окна                     { "view": "label", "label": "Electron + Webix + D3.js", css:"head_win" },                     // Кнопка "свернуть окно"                     { "label": "-", "view": "button", "height": 38, "width": 40, id:"min-bt" },                     // Кнопка "развернуть окно"                     { "label": "+", "view": "button", "height": 38, "width": 40, id:"max-bt" },                     // Закрыть приложение                     { "label": "x", "view": "button", "height": 38, "width": 40, id:"close-bt" }                 ]             },             // view : myview - второй элемент нашего интерфейса, в котором мы будем рисовать график.             { "view": "myview", id: "d3_chart" },             {                 "cols": [                     // view : slider - 3 элемент. Будет менять величину амплитуды sin                     {                         "label": "Amplitude", "title": "#value#", "value": 50, "view": "slider", id: "slider_amplitude",                     },                     // view : slider - 4 элемент. Будет менять величину смещения графика sin относительно оси Y                     {                         "label": "Bias", "title": "#value#", "value": 0, "view": "slider", "height": 38, id: "slider_scope", min:-50, max:50, step:1,                     },                     // view : slider - 5 элемент. Будет менять величину частоты sin                     {                         "label": "Frequency", "title": "#value#", "value": 0.005, "view": "slider", "height": 38, id: "slider_freq", min:0, max:0.1, step:0.001,                     }                 ]             }         ]     } )  // Закрытие окна $$("close-bt").attachEvent("onItemClick", () => {     const window = remote.getCurrentWindow();     window.close(); })  // Свернуть окно $$("min-bt").attachEvent("onItemClick", () => {     const window = remote.getCurrentWindow();     window.minimize(); })  // Распахнуть окно $$("max-bt").attachEvent("onItemClick", () => {     const window = remote.getCurrentWindow();     if (!window.isMaximized()) {         window.maximize();     } else {         window.unmaximize();     } }) 

Рассмотрим более подробно, какие параметры в себя включает «{ «view»: «myview», id: «d3_chart» }»:
— view: myview – тип «view», пока не существует мы его еще не создали;
— id: d3_chart – «Webix» id, будем его использовать для обращения к нашему элементу.
Поясню еще параметры элемента «slider» для полноты картины:
— view: slider – тип «view», слайдер;
— label: «текст» – ярлык, который будет отображаться на слайдере;
— title: «#value» – текущее отображаемое значение слайдера;
— value: «значение» – значение слайдера по умолчанию;
— min: «значение» – минимальное значение слайдера;
— max: «значение» – максимальное значение слайдера";
— step: «значение» – шаг изменения величины слайдера.
Более подробно о всех параметрах элементов «Webix» можно почитать вот тут docs.webix.com/desktop__components.html

Создадим «myview»

Теперь создадим файл «view_chart.js» в котором и определим наш новый тип «myview» и открываем его в «VSC».


Рис. 6 – Приступаем к редактированию view_chart.js

Добавим в него следующий код:

webix.protoUI({     name: "myview",     $init: function () {     },     redraw: function (a, b, c) {     }, }, webix.ui.view); 

Функция «webix.protoUI» предназначена для создания кастомных элементов «Webix» базирующихся на стандартных элементах. Рассмотрим параметры, которые передаются этой функции:

  • name – имя создаваемого элемента;
  • $init – функция, которая будет запускаться при создании элемента. Другими словами, это конструктор;
  • redraw – наша с Вами функция, которая будет отвечать за перерисовку графика функции. Ей будем передавать значения параметров наших переменных “a, b и c”
  • webix.ui.view – родитель нашего элемента.

Более подробно о webix.protoUI можно почитать вот тут
Итак, когда каркас нашего “view” готов напишем пару функций которые, будут отвечать за прорисовку графика sin.
Функция расчета значений функции sin будет иметь вид:

function calcsin(a, b, c) {     dataset = [];     for (var i = 1; i < 360; i++) {         dataset.push({ "x": i, "y": a * Math.sin(b * i) + c });     }; }; 

Тут все очень просто, подставляем значения параметров в математическую функцию sin и результат сохраняем в массиве. Добавим ее в конец файла «view_chart.js».
Далее добавим в конец файла «view_chart.js» функцию, которая будет чертить график используя фреймворк D3.js она будет иметь следующий вид:

function drawSin() {     $(document).ready(function () {         width = $("#mycontent").width() - margin.left - margin.right         height = $("#mycontent").height() - margin.top - margin.bottom;         var xScale = d3.scaleLinear()             .domain([0, 360])              .range([0, width]);           var yScale = d3.scaleLinear()             .domain([-100, 100])              .range([height, 0]);            var line = d3.line()             .x(function (d, i) { return xScale(d.x); })              .y(function (d) { return yScale(d.y); })             .curve(d3.curveMonotoneX)          var svg = d3.select("#mycontent").append("svg")             .attr("float", "center")             .attr("class", "webix_chart")             .attr("width", width + margin.left + margin.right)             .attr("height", height + margin.top + margin.bottom)             .append("g")             .attr("transform", "translate(" + margin.left + "," + margin.top + ")")          svg.append("g")             .attr("class", "x axis")             .attr("transform", "translate(0," + height/2 + ")")             .call(d3.axisBottom(xScale));           svg.append("g")             .attr("class", "y axis")             .call(d3.axisLeft(yScale))          svg.append("path")              .datum(dataset)              .attr("class", "line")              .attr("d", line);      })  } 

Порядок работы приведенной функции, следующий:
— вычисляем размеры графика относительно элемента div в который он упакован «width» и «height»;
— масштабируем значения графика по «X» и «Y»;
— создаем «html» элемент «svg» в котором рисуем ось «X» ось «Y» и непосредственно сам график;
Теперь, когда функции для расчета и отрисовки нашего графика готовы, подключим пару библиотек и объявим несколько переменных в начале файла «view_chart.js»:

// Подключаем ранее скачанную библиотеку D3.js const d3 = require("./libs/d3") // Подключаем библиотеку JQuery $ = require('jquery') // Массив в котором будем хранить расчетные значения по оси X и по оси Y var dataset = []; // Переменные которые будут хранить размеры нашего графика и отступы var margin = { top: 50, right: 50, bottom: 50, left: 50 }     , width = $("#mycontent").width() - margin.left - margin.right      , height = $("#mycontent").height() - margin.top - margin.bottom; 

В переменных «width» и «height» будет храниться ширина и высота графика с учетом отступов.
Далее вернемся обратно к функции «webix.protoUI» и пропишем его функционал:

webix.protoUI({     name: "myview",     $init: function () { // Инициализация View         // Добавляем блок div с id=mycontent. В этом блоке будем рисовать график         this.$view.innerHTML = "<div id='mycontent'></div>"         // Вызываем функцию calcsin с начальными параметрами и заполняем массив data расчетными значениями         calcsin(60, 0.05, 0)         // Вызываем функцию drawSin и чертим график по данным из массива data         drawSin()     },     redraw: function (a, b, c) { // Функция для перерисовки графика         // Удаляем старый график         $("#mycontent").html("")         // Выполняем очередной расчет и заполняем массив data         calcsin(a, b, c)         // Чертим график         drawSin()     }, }, webix.ui.view); 

Наполнили содержимым две функции. Первая «init» выполняется только один раз при создании «view». Вторая будет вызваться, когда нам нужно будет перерисовать график.
Теперь полное содержимое файла «view_chart.js» будет выглядеть вот так:

// Подключаем ранее скачанную библиотеку D3.js const d3 = require("./libs/d3") // Подключаем библиотеку JQuery $ = require('jquery') // Массив в котором будем хранить расчетные значения по оси X и по оси Y var dataset = []; // Переменные которые будут хранить размеры нашего графика и отступы var margin = { top: 50, right: 50, bottom: 50, left: 50 }     , width = $("#mycontent").width() - margin.left - margin.right      , height = $("#mycontent").height() - margin.top - margin.bottom;  webix.protoUI({     name: "myview",     $init: function () { // Инициализация View         // Добавляем блок div с id=mycontent. В этом блоке будем рисовать график         this.$view.innerHTML = "<div id='mycontent'></div>"         // Вызываем функцию calcsin с начальными параметрами и заполняем массив data расчетными значениями         calcsin(60, 0.05, 0)         // Вызываем функцию drawSin и чертим график по данным из массива data         drawSin()     },     redraw: function (a, b, c) { // Функция для перерисовки графика         // Удаляем старый график         $("#mycontent").html("")         // Выполняем очередной расчет и заполняем массив data         calcsin(a, b, c)         // Чертим график         drawSin()     }, }, webix.ui.view);    function drawSin() {     $(document).ready(function () {         width = $("#mycontent").width() - margin.left - margin.right         height = $("#mycontent").height() - margin.top - margin.bottom;         var xScale = d3.scaleLinear()             .domain([0, 360])              .range([0, width]);           var yScale = d3.scaleLinear()             .domain([-100, 100])              .range([height, 0]);            var line = d3.line()             .x(function (d, i) { return xScale(d.x); })              .y(function (d) { return yScale(d.y); })             .curve(d3.curveMonotoneX)          var svg = d3.select("#mycontent").append("svg")             .attr("float", "center")             .attr("class", "webix_chart")             .attr("width", width + margin.left + margin.right)             .attr("height", height + margin.top + margin.bottom)             .append("g")             .attr("transform", "translate(" + margin.left + "," + margin.top + ")")          svg.append("g")             .attr("class", "x axis")             .attr("transform", "translate(0," + height/2 + ")")             .call(d3.axisBottom(xScale));           svg.append("g")             .attr("class", "y axis")             .call(d3.axisLeft(yScale))          svg.append("path")              .datum(dataset)              .attr("class", "line")              .attr("d", line);      })  }  function calcsin(a, b, c) {     dataset = [];     for (var i = 1; i < 360; i++) {         dataset.push({ "x": i, "y": a * Math.sin(b * i) + c });     }; }; 

Далее нужно подключить содержимое файла «view_chart.js» в файле «renderer.js». Для чего нужно добавить в начало файла «renderer.js» строчку «require("./view_chart.js")» как показано ниже:

// Подключаем модуль электрона const { remote } = require('electron') // Получаем указатель на окно браузера где будет производиться отображение нашего интерфейса let WIN = remote.getCurrentWindow() // Подключаем фреймворк webix const webix = require('./libs/webix.min.js') //Подключаем фреймворк JQuery $ = require('jquery') // Подключаем view_chart.js require("./view_chart.js") 

Теперь необходимо добавить пару стилей в файл «styles.css», открываем его и добавляем стиль «svg path» и «#mycontent» как показано ниже:

.head_win {     -webkit-app-region: drag; }  /* Стиль графика */ svg path{     /* Цвет обводки */     stroke: #666;     /* Без заливки */     fill: none;     /* Толщина линии обводки */     stroke-width: 1; }  /* Стиль блока mycontent, в котором чертим график */ #mycontent {      /* Выравнивание текста по середине */     text-align: center;     /* Без скроллбаров */     overflow: none;     /* Бордюр  */     border: 1px solid black;     /* Цвет заливки фона блока */     background-color: antiquewhite;     /* Высота блока */     height: calc(100% - 2px); } 

После того, когда закончили со стилями, можно попробовать запустить наше приложение и посмотреть, что у нас получилось. Итак, жмем “F5” и после на экране должно появиться окошко как показано на рисунке 7.


Рис. 7 – Окно нашего приложения

Что мы видим на нем? Верхняя часть — это стандартный тулбар «Webix». Средняя часть — график, созданный нами в «myview». Нижняя часть — три слайдера, которые мы должны научить менять значения параметров нашей функции.

Заставим слайдеры работать

Теперь перейдем к шагу, в котором будем перерисовывать график в средней части нашего приложения в зависимости от значений слайдеров «Amplitude», «Bias» и «Frequency». Для чего добавим к слайдерам обработчик события «onChange» как показано ниже:

// view : slider - 3 элемент. Будет менять величину амплитуды sin                     {                         "label": "Amplitude", "title": "#value#", "value": 50, "view": "slider", id: "slider_amplitude",                         on: {                             onChange: function () {                                 $$("d3_chart").redraw($$("slider_amplitude").getValue(), $$("slider_freq").getValue(), $$("slider_scope").getValue());                             }                         }                     },                     // view : slider - 4 элемент. Будет менять величину смещения графика sin относительно оси Y                     {                         "label": "Bias", "title": "#value#", "value": 0, "view": "slider", "height": 38, id: "slider_scope", min:-50, max:50, step:1,                         on: {                             onChange: function () {                                 $$("d3_chart").redraw($$("slider_amplitude").getValue(), $$("slider_freq").getValue(), $$("slider_scope").getValue());                             }                         }                     },                     // view : slider - 5 элемент. Будет менять величину частоты sin                     {                         "label": "Frequency", "title": "#value#", "value": 0.005, "view": "slider", "height": 38, id: "slider_freq", min:0, max:0.1, step:0.001,                         on: {                             onChange: function () {                                 $$("d3_chart").redraw($$("slider_amplitude").getValue(), $$("slider_freq").getValue(), $$("slider_scope").getValue());                             }                         }                     } 

В данном обработчике будем обращаться к нашему элементу по его «id» и вызывать созданную ранее нами функцию «redraw» для перерисовки графика передавая ей текущие значения слайдеров. Жмем “F5” запускаем наше приложение и пробуем изменить значения (рис. 8).


Рис. 8 – Проверка функционала слайдеров

Тянем график вслед за окном

И напоследок добавим функцию, которая будет адаптировать наш график под размеры окна, когда оно будет изменяться. Для чего открываем в редакторе файл «view_chart.js» и добавляем в его конец обработчик, который отлавливает изменения окна как показано ниже:

window.onresize = function ( e ) {        $("#mycontent").html("")     drawSin() } 

Данная функция срабатывает при изменении размера окна. Ее функционал сводиться к очистки содержимого блока «mycontent» и перерисовки графика (вызов функции «drawSin()»). Каждый разы при вызове функция «drawSin()» переменным «width» и «height» присваиваться текущая информация о размерах (высоте и ширине) блока «mycontent» после чего график будет изменять масштаб под размеры блока.

Заключение

На этом все. Надеюсь, данная информация будет Вам полезна. Спасибо! Исходники можно скачать вот тут .

ссылка на оригинал статьи https://habr.com/ru/post/500404/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *