Документация Engee

Создание первой информационной панели

Информационная панель — это тип реактивного приложения, в котором визуализируются данные и с которым пользователь может взаимодействовать с помощью таких элементов управления, как кнопки или ползунки. На каждое действие пользователя приложение реагирует путем выполнения кода, производящего операции с базовыми данными и быстро отражающего изменения в пользовательском интерфейсе. Все это происходит в реальном времени без необходимости перезагружать страницу.

В этом руководстве вы узнаете, как создать информационную панель, на которой выводится статистика по вектору случайных чисел и которая позволяет пользователю изменять длину вектора. Код основан преимущественно на функциях реактивного пользовательского интерфейса, реализованных в пакете Stipple.jl, который уже включен в метапакет GenieFramework.jl.

preview

Структура проекта

При создании приложения Genie обычно предполагается, что у вас уже есть некий код для анализа данных, который вы хотите превратить в веб-приложение. В этой демонстрации мы начнем с модуля StatisticAnalysis со следующим содержимым.

module StatisticAnalysis

export gen_numbers, calc_mean

function gen_numbers(N::Int)
    return rand(N)
end

function calc_mean(x::Vector{Float64})
    return round(sum(x) / length(x); digits=4)
end
end

Создайте папку проекта для демонстрационного приложения, а в ней создайте папку lib/ и поместите в нее файл StatisticAnalysis.jl. Содержимое этой папки будет автоматически загружаться в модуль Main при запуске приложения.

Кроме того, у нас есть файл app.jl, который является главной точкой входа в приложение, и именно в нем мы будем реализовывать информационную панель. Создайте файл в корне проекта со следующим содержимым:

module App
using Main.StatisticAnalysis
using GenieFramework, PlotlyBase
@genietools

@app begin
    #здесь будет реактивный код
end

function ui()
    p("") #инициализируется пустым абзацем
end

@page("/", ui)
end

Кодекс делится на четыре части:

  • операторы импорта GenieFramework, PlotlyBase и кода для анализа данных в StatisticAnalysis.jl;

  • реактивный код в блоке @app для управления интерактивностью информационной панели;

  • определение пользовательского интерфейса в функции ui;

  • определение маршрута с помощью @page для связывания URL-адреса с представлением страницы, предоставляемым функцией ui.

Структура файлов должна выглядеть так:

├── app.jl
└── lib
    └── StatisticAnalysis.jl

Чтобы запустить приложение, выполните в REPL Julia следующие команды.

using GenieFramework; Genie.loadapp(); up()

Сервер запустится на порте 8000, и приложение будет доступно по адресу https://localhost:8000. Чтобы остановить сервер, выполните в REPL команду down().

Теперь добавим код пользовательского интерфейса и реактивный код для реализации следующих элементов:

  • ползунок для управления длиной вектора случайных чисел;

  • текстовая строка для отображения среднего значения элементов вектора;

  • гистограмма вектора.

Ползунок и среднее значение — пользовательский интерфейс

Пользовательский интерфейс можно создать на чистом языке Julia, используя малокодовый API, предоставляемый пакетом StippleUI.jl, который включается автоматически при выполнении директивы using GenieFramework. Он предоставляет ряд вызовов, таких как textfield или slider, для формирования HTML-кода компонента пользовательского интерфейса:

julia> slider(1:1:1000, :N)
"<q-slider :min=1 v-model=\"N\" :max=1000 :step=1></q-slider>"

Полный список доступных компонентов см. в коллекции компонентов и API.

Чтобы включить в пользовательский интерфейс компонент ползунка и компонент для отображения среднего значения, добавьте в функцию ui следующий код:

function ui()
    row([
    cell(class="st-col col-3", [
        h1("A simple dashboard"),
        slider(1:1:1000, :N),
        p("The average of {{N}} random numbers is {{m}}", class="st-module"),
        ])
    ])
end

[discrete.text-center] ====== A simple dashboard

The average of {{N}} random numbers is {{m}}

</div> </div>

Здесь задается макет страницы с строкой и ячейкой с CSS-классами st-col, col-3 для установки ширины столбцов. Внутри ячейки отображаются заголовок, слайдер и абзац. Компонент slider принимает в качестве параметров диапазон возможных значений и символьное имя реактивной переменной, в которой будет храниться выбранное число. Эта переменная пока не существует. Мы добавим ее в реактивный код позже.

Синтаксис {{variable}} используется в абзаце p для отображения содержимого реактивных переменных. Более того, этот синтаксис принимает любое допустимое выражение JavaScript, поэтому можно написать, например, {{m.toFixed(2)}} для отображения среднего значения с двумя знаками после запятой.

Чтобы изучить код HTML, сгенерированный функцией ui, можно вызвать его в REPL. Результат будет таким:

<div class="row">
   <div class="st-col col-3 st-col col">
      <h1>A simple dashboard</h1>
      <q-slider :min=1 v-model="N" :max=1000 :step=1 ></q-slider>
      <p class="st-module">The average of {{N}} random numbers is {{m}}</p>
   </div>
</div>

Так как переменные N, m в коде пока являются просто заполнителями, пользовательский интерфейс работать не будет. Давайте добавим реактивный код, чтобы сделать его интерактивным.

Ползунок и среднее значение — логика

При перемещении ползунка должно устанавливаться новое значение длины вектора и отображаться обновленное среднее значение. Для этого сначала объявим реактивную переменную N в блоке @app следующим образом.

@app begin
   @in N = 0
end

Пометка @in делает переменную реактивной и доступной для записи из пользовательского интерфейса. Каждое изменение значения в пользовательском интерфейсе в браузере будет автоматически передаваться в код Julia. И наоборот, любое изменение в коде Julia будет передаваться в пользовательский интерфейс.

Затем добавим выходную переменную только для чтения, в которой будет храниться среднее значение:

@out m = 0.0

Переменные с пометкой @out можно изменять только из кода Julia. Изменения, внесенные в браузере, не будут передаваться в бэкенд. Обратите внимание, что реактивные переменные всегда должны инициализироваться значением соответствующего типа.

Кроме того, вы можете использовать макрос @private для определения нереактивных переменных, которые не отображаются в пользовательском интерфейсе. Они применяются для хранения данных, не предназначенных для обмена между пользователями.

Наконец, с помощью макроса @onchange можно объявить реактивный обработчик, который генерирует новый вектор и вычисляет его среднее значение при изменении N:

@app begin
    @in N = 0
    @out m = 0.0
    @onchange N begin
        x = gen_numbers(N)
        m = calc_mean(x)
    end
end

С помощью @in, @out и @onchange можно легко реализовать любые интерактивные возможности. Дополнительные сведения см. в справке по реактивности.

Теперь у вас должен получиться работающий реактивный пользовательский интерфейс! Давайте добавим гистограмму.

Гистограмма

Чтобы отобразить график в пользовательском интерфейсе, добавьте вызов plot следующим образом.

row([
    cell(class="st-col col-3", [
        h1("A simple dashboard"),
        slider(1:1000, :N),
        p("The average of {{N}} random numbers is {{m}}", class="st-module"),
        plot(:trace, layout=:layout)
    ])
])

В результате будет добавлен график Plotly: переменная trace содержит данные для построения графика, а layout определяет параметры стиля.

Теперь объявим пустую трассировку гистограммы и переменную макета следующим образом.

@app begin
    @out trace = [histogram(x=[])]
    @out layout = PlotlyBase.Layout(title="Histogram plot")
    . . .
end

Далее добавим код для обновления трассировки в реактивном обработчике при изменении N.

@onchange N begin
    . . .
    trace = [histogram(x=x)]
end

Окончательный реактивный код в app.jl выглядит так:

@app begin
    @in N = 0
    @out m = 0.0
    @out trace = []
    @out layout = PlotlyBase.Layout(title="Histogram plot")
    @onchange N begin
        x = gen_numbers(N)
        m = calc_mean(x)
        trace = [histogram(x=x)]
    end
end

Вот и все, вы создали свою первую информационную панель! Дополнительные сведения о построении графиков см. на странице справки. Далее вы можете продолжить ознакомление с руководствами, чтобы добавить дополнительные возможности, например несколько страниц или API.