Введение
В предыдущей статье мы рассмотрели с Вами вопрос интеграции фреймворка «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» и я решил его интегрировать в приложение.
Инструменты
- Visual Studio Code – редактор code.visualstudio.com.
- Node.JS – скачать вот отсюда nodejs.org/en
- Electron – установить «npm install --save-dev electron».
- Webix – скачать вот отсюда ru.webix.com.
- JQuery – установить «npm jquery»
- D3.js – скачать вот отсюда d3js.org
- Исходный код (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» после чего график будет изменять масштаб под размеры блока.
Заключение
На этом все. Надеюсь, данная информация будет Вам полезна. Спасибо! Исходники можно скачать вот тут .