Pull to refresh

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

Reading time14 min
Views1.9K

Введение


В предыдущей статье мы рассмотрели с Вами вопрос интеграции фреймворка «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» после чего график будет изменять масштаб под размеры блока.

Заключение


На этом все. Надеюсь, данная информация будет Вам полезна. Спасибо! Исходники можно скачать вот тут .
Tags:
Hubs:
If this publication inspired you and you want to support the author, do not hesitate to click on the button
+3
Comments0

Articles

Change theme settings