1.4.3. Создание диаграмм

1.4.3.1. XML-дескриптор экрана

Откройте в CUBA Studio вкладку Screens и создайте экран в модуле web. Введите значение com/sample/library/web/charts/statistics.xml в поле Reference. В полях Id, Controller Name и Messages Pack будут сгенерированы подходящие значения. Сохраните изменения. Далее перейдите на вкладку XML и замените ее содержимое на следующий код:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<window xmlns="http://schemas.haulmont.com/cuba/window.xsd"
        caption="msg://statistic"
        class="com.sample.library.web.charts.statistics.Statistics"
        messagesPack="com.sample.library.web.charts.statistics"
        xmlns:chart="http://schemas.haulmont.com/charts/charts.xsd">
    <dsContext>
        <collectionDatasource id="pieDs"
                              class="com.sample.library.entity.BooksByGenre"
                              refreshMode="NEVER"
                              view="_local"/>
    </dsContext>
    <layout spacing="true">
        <tabSheet height="100%"
                  width="100%">
            <tab id="pieChartTab"
                 caption="msg://booksByGenres">
                <hbox expand="pieChart"
                      height="100%"
                      margin="true"
                      spacing="true"
                      width="100%">
                    <chart:pieChart id="pieChart"
                                    angle="30"
                                    balloonText="[[genre]] - [[percents]]%"
                                    datasource="pieDs"
                                    depth3D="15"
                                    height="100%"
                                    titleField="genre"
                                    valueField="countOfBooks"
                                    width="100%">
                        <chart:exportConfig menuTop="0px">
                            <chart:menuItems>
                                <chart:menu format="PNG"
                                            icon="VAADIN/resources/amcharts/images/export.png"/>
                            </chart:menuItems>
                        </chart:exportConfig>
                    </chart:pieChart>
                    <table columnControlVisible="false"
                           height="100%"
                           width="300px">
                        <columns>
                            <column id="genre"/>
                            <column id="countOfBooks"/>
                        </columns>
                        <rows datasource="pieDs"/>
                    </table>
                </hbox>
            </tab>
            <tab id="stackedChartTab"
                 caption="msg://countOfBooksByPublisherAndYear"
                 spacing="true">
                <chart:serialChart id="stackedChart"
                                   categoryField="year"
                                   height="100%"
                                   plotAreaFillAlphas="0.1"
                                   width="100%">
                    <chart:chartCursor/>
                    <chart:legend markerType="TRIANGLE_RIGHT"
                                  position="TOP"
                                  valueAlign="LEFT"/>
                    <chart:categoryAxis startOnAxis="true"
                                        title="msg://year"/>
                    <chart:valueAxes>
                        <chart:axis gridAlpha="0.07"
                                    position="LEFT"
                                    stackType="REGULAR"
                                    title="msg://countOfBooks"/>
                    </chart:valueAxes>
                    <chart:exportConfig menuTop="0px">
                        <chart:menuItems>
                            <chart:menu format="PNG"
                                        icon="VAADIN/resources/amcharts/images/export.png"/>
                        </chart:menuItems>
                    </chart:exportConfig>
                </chart:serialChart>
            </tab>
        </tabSheet>
    </layout>
</window>

В корневой элемент дескриптора экрана добавлен атрибут xmlns:chart:

<window xmlns:chart="http://schemas.haulmont.com/charts/charts.xsd"
    ...
    >

Созданный нами экран содержит панель с двумя вкладками. На первой находится круговая диаграмма, представляющая распределение книг по жанрам:

<chart:pieChart id="pieChart"
                    angle="30"
                    balloonText="[[genre]] - [[percents]]%"
                    datasource="pieDs"
                    depth3D="15"
                    height="100%"
                    titleField="genre"
                    valueField="countOfBooks"
                    width="100%">
        <chart:exportConfig menuTop="0px">
            <chart:menuItems>
                <chart:menu format="PNG"
                            icon="VAADIN/resources/amcharts/images/export.png"/>
            </chart:menuItems>
        </chart:exportConfig>
    </chart:pieChart>

Даграмма получает данные из источника pieDs, указанного в атрибуте datasource. Для отображения названий и значений используются атрибуты genre и countOfBooks сущности BooksByGenre, список экземпляров которой находится в источнике данных. С источником данных соединена также таблица, поэтому она отображает те же данные, что и диаграмма.

Компонент pieChart содержит следующие атрибуты:

  • angle - определяет угол наклона диаграммы. Может принимать значения от 0 до 90.

  • balloonText - определяет текст всплывающей подсказки при наведении на отделяемую часть диаграммы. Доступны для использования тэги [[value]], [[title]], [[persents]], [[description]], а также ключи из DataItem, список которых хранится в экземпляре DataProvider, либо имена атрибутов сущности в источнике данных.

  • depth3D - толщина диграммы. При использовании совместно с атрибутом angle позволяет создать эффект объема.

  • titleField - ключ из набора пар, содержащихся в объектах DataItem, список которых хранится в экземпляре DataProvider, по которому будет взято значение для заголовка сектора в круговой диаграмме.

  • valueField - ключ из набора пар, содержащихся в объектах DataItem, список которых хранится в экземпляре DataProvider, по которому будет взято значение для сектора.

Компонент pieChart содержит следующие элементы:

  • chart:legend - определяет легенду графика. Атрибут position определяет положение легенды относительно диаграммы, markerType - форму маркера, помечающего информацию о каждом секторе диаграммы.

  • chart:exportConfig - добавляет возможность сохранить полученный график. Атрибуты menuTop, menuLeft, menuRight, menuBottom позволяют задать положение кнопки сохранения. В примере кнопка располагается в правом верхнем углу диаграммы.

    Элемент chart:menuItems содержит настройки сохранения. Кроме используемого в примере формата png поддерживается сохранение в форматах jpg, svg и pdf. Атрибут icon содержит путь к изображению, которое будет использоваться в качестве кнопки импорта.

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

<chart:serialChart id="stackedChart"
                       categoryField="year"
                       height="100%"
                       width="100%">
       <chart:chartCursor/>
       <chart:legend markerType="TRIANGLE_RIGHT"
                     position="TOP"
                     valueAlign="LEFT"/>
       <chart:categoryAxis startOnAxis="true"
                           title="msg://year"/>
       <chart:valueAxes>
          <chart:axis position="LEFT"
                      stackType="REGULAR"
                      title="msg://countOfBooks"/>
       </chart:valueAxes>
       <chart:exportConfig menuTop="0px">
          <chart:menuItems>
             <chart:menu format="PNG"
                         icon="VAADIN/resources/amcharts/images/export.png"/>
          </chart:menuItems>
       </chart:exportConfig>
    </chart:serialChart>

Эта диаграмма получает данные через DataProvider, созданный в контроллере (см. ниже), поэтому атрибут datasource не указан.

Атрибуты chart:serialChart:

  • categoryField - ключ из набора пар, содержащихся в объектах DataItem, список которых хранится в экземпляре DataProvider, по которому будут взяты значения для подписи оси категорий.

Элементы chart:serialChart:

  • chart:chartCursor - необязательный элемент, добавляющий на график курсор, который следует за движениями мыши и отображает всплывающую подсказку со значением графика, соответствующим точке нахождения курсора.

  • chart:categoryAxis - элемент, описывающий ось категорий. Установка атрибуту startOnAxis значения true дает указание начинать отрисовывать график сразу от оси значений. По умолчанию этот атрибут имеет значение false. В этом случае между осью значений и графиком имеется некоторый помежуток. Атрибут title задает заголовок оси категорий.

  • chart:valueAxes - элемент, описывающий вертикальные оси значений. В данном случае используется только одна ось, описываемая элементом chart:axis. Атрибут position задает положение оси значений относительно диаграммы. Установка атрибуту stackType значения REGULAR говорит о том, что используется диаграмма с накоплением. По умолчанию значение этого атрибута - none, в таком случае используется диаграмма без накопления.

1.4.3.2. Контроллер экрана

Перейдите на вкладку Controller и замените ее содержимое на следующий код:

package com.sample.library.web.charts.statistics;

import com.haulmont.charts.gui.amcharts.model.Graph;
import com.haulmont.charts.gui.amcharts.model.GraphType;
import com.haulmont.charts.gui.amcharts.model.charts.SerialChart;
import com.haulmont.charts.gui.amcharts.model.data.ListDataProvider;
import com.haulmont.charts.gui.amcharts.model.data.MapDataItem;
import com.haulmont.charts.gui.components.charts.Chart;
import com.haulmont.cuba.gui.components.AbstractWindow;
import com.haulmont.cuba.gui.data.CollectionDatasource;
import com.sample.library.entity.BooksByGenre;
import com.sample.library.service.StatisticsService;

import javax.inject.Inject;
import java.util.List;
import java.util.Map;
import java.util.UUID;

public class Statistics extends AbstractWindow {

    @Inject
    private CollectionDatasource<BooksByGenre, UUID> pieDs;

    @Inject
    private Chart stackedChart;

    @Inject
    private StatisticsService statisticsService;

    public void init(Map<String, Object> param) {
        initPieChart();
        initStackedChart();
    }

    private void initPieChart() {
        pieDs.refresh();

        List<BooksByGenre> booksByGenreList = statisticsService.getCountOfBooksByGenre();
        for (BooksByGenre booksByGenre : booksByGenreList) {
            pieDs.includeItem(booksByGenre);
        }
    }

    private void initStackedChart() {
        List<String> allPublishers = statisticsService.getTopPublishers(5);
        Map<Integer, Map<String, Long>> structuredData = statisticsService.getCountOfBooksByPublisherAndYear();

        ListDataProvider dataProvider = new ListDataProvider();

        for (Map.Entry<Integer, Map<String, Long>> entry : structuredData.entrySet()) {
            MapDataItem mapDataItem = new MapDataItem().add("year", entry.getKey());

            for (String publisher : allPublishers) {
                if (entry.getValue().containsKey(publisher)) {
                    mapDataItem.add(publisher, entry.getValue().get(publisher));
                } else {
                    mapDataItem.add(publisher, 0);
                }
            }
            dataProvider.addItem(mapDataItem);
        }

        SerialChart stackedChartConfiguration = (SerialChart) stackedChart.getConfiguration();
        stackedChartConfiguration.setDataProvider(dataProvider);

        Graph[] graphs = new Graph[allPublishers.size()];
        int i = 0;
        for (String publisher : allPublishers) {
            Graph publisherGraph = new Graph();
            publisherGraph.setFillAlphas(0.6);
            publisherGraph.setLineAlpha(0.4);
            publisherGraph.setTitle(publisher);
            publisherGraph.setType(GraphType.LINE);
            publisherGraph.setValueField(publisher);
            publisherGraph.setBalloonText(publisher + " - [[year]] year: [[" + publisher + "]] books");

            graphs[i] = publisherGraph;
            i++;
        }
        stackedChartConfiguration.addGraphs(graphs);
    }
}

В методе initPieChart() происходит заполнение источника данных pieDs данными, полученными из сервиса. Метод refresh() производит инициализацию источника данных. Этот метод необходимо вызвать, несмотря на атрибут refreshMode="NEVER", установленный в XML-дескрипторе.

В методе initStackedChart(List<String> allPublishers) происходит установка данных в диаграмму с накоплением. Диаграммы подобного типа показывают отношение отдельных составляющих к их совокупному значению. В метод передается список издателей, выпустивших наибольшее количество экземпляров книг, находящихся в библиотеке. Для каждого издателя строится график, представляющий количество книг, выпущенных в разные годы. Графику соответствует экземпляр класса Graph. Рассмотрим методы настройки графиков:

  • setFillAlphas(0.6) - устанавливает степень непрозрачности заливки.

  • setLineAlpha(0.4) - устанавливает толщину линии графика. Допустимые значения от 0 до 1.

  • setTitle(publisher) - устанавливает заголовок графика.

  • setType(GraphType.LINE) - устанавливает тип отбражения данных.

  • setValueField(publisher) - ключ из набора пар, содержащихся в объектах DataItem, список которых хранится в экземпляре DataProvider, по которому будет взято значение для графика.

  • setBaloonText(publisher + " - [[year]] year: [[" + publisher + "]] books") - устанавливает значение всплывающей подсказки.

1.4.3.3. Скриншоты диаграмм

Посмотрим, как созданный нами экран выглядит в работающем приложении. Добавьте созданный экран в меню, затем пересоберите проект командой Run -> Restart application server и зайдите в систему. Откройте экран со статистикой.

Рисунок 1.2. Круговая диаграмма

Круговая диаграмма

Рисунок 1.3. Диаграмма с накоплением

Диаграмма с накоплением

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