11 июня 2011 г.

Прикручиваем Adobe Flex к приложению на Delphi.



Adobe Flex предоставляет весьма удобные и наглядные средства визуализации данных такие как OLAP кубы разного рода диаграммы с красивыми эффектами. Мне как то захотелось использовать такую диаграмму в своем приложении написанном на Delphi. Например.
Alternative content
Get Adobe Flash player
Конечно я знаю что Delphi имеется аналогичные компоненты но они как мне кажется в смысле дизайна более «серым»и на фоне «Flex»cовых компонентов. Попробуем встроить Flex приложение в Delphi и передать в него данные для вывода графика.
Поскольку FlashDevelop написан на C# он требует .NET Framework и JRE чтобы скомпилировать Flex приложение. Можно скачать Beta 4-ой версии или стабильную 3-ю при установке нужно отметить «Install Flex SDK» при этом установщик скачает и распакует Adobe Flex SDK. Я использовал бету.
Пишем наше Flex приложение.
Запускаем FlashDevelop. Переходим меню Project > New Project, выбираем Flex 4 Project, жмем OK. Пишем имя нашего приложения(у меня «Flex»).
Вот код нашего приложения.
<?xml version="1.0" encoding="utf-8"?>
<!-- Пример диаграммы встраиваемой в программу Delphi-->
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
               xmlns:mx="library://ns.adobe.com/flex/mx"
               xmlns:s="library://ns.adobe.com/flex/spark"
               initialize="init()">

    <fx:Declarations>
        <!--Небольшой Эффект -->
        <mx:SeriesSlide id="slideIn"
                        duration="1500"
                        direction="up" />
    </fx:Declarations>
    <fx:Script>
        <![CDATA[
            import flash.external.ExternalInterface;
            import mx.collections.ArrayCollection;
            
            [Bindable]
            public var danniye:ArrayCollection = new ArrayCollection;
            
            public function fromDelphi(massiv:Array):void
            {
                //Записиваваем полученные данные в массив для Графика
                danniye = new ArrayCollection(massiv);
            }            
            //Эта функция вызывается при инициазизации флекс приложения
            public function init():void
            {
                //Регистрируем внешную функцию
                ExternalInterface.addCallback("fromDelphi", fromDelphi);
            }
        ]]>
    </fx:Script>
    <s:layout>
        <s:VerticalLayout />
    </s:layout>
    <s:Panel title='Пример "нереального" бизнесса'>
        <s:layout>
            <s:VerticalLayout />
        </s:layout>
        <mx:ColumnChart id="myChart"
                        dataProvider="{danniye}"
                        showDataTips="true">
            <mx:horizontalAxis>
                <mx:CategoryAxis dataProvider="{danniye}"
                                 categoryField="MONTH" />
            </mx:horizontalAxis>
            <mx:series>
                <mx:ColumnSeries xField="MONTH"
                                 yField="DOHOD"
                                 displayName="Доходы"
                                 showDataEffect="slideIn" />
                <mx:ColumnSeries xField="MONTH"
                                 yField="RASHOD"
                                 displayName="Расход"
                                 showDataEffect="slideIn" />
                <mx:ColumnSeries xField="MONTH"
                                 yField="PRIB"
                                 displayName="Прибыль"
                                 showDataEffect="slideIn" />
            </mx:series>
        </mx:ColumnChart>
        <mx:Legend dataProvider="{myChart}" />
    </s:Panel>
</s:Application>
Для компиляции и запуска жмем F5.
Delphi приложение.
И так запустим Delphi и начнем новый проект. Свяжем наше приложение с базой данных. Для простоты я использовал ClientDataSet а данные беру из XML файл в формате cds-xml. Бросаем компонент форму указываем путь к XML файлу. Бросаем DataSource свойство DataSet устанавливаем равным ClientDataSet.
Теперь добавляем идем меню Component > Import Active X Control

В появившемся окне выбираем Shockwave Flash и нажимаем кнопку Install. В следующих окнах нажимаем OK и Yes.
Бросаем в окно программы компонент ShockwaveFlash из набора Active X. Добавим в uses XMLIntf, XMLDoc, StdCtrls и напишем процедуру для вызова функции которая будет находится внутри нашей флешки.
procedure TForm1.CdsToXml(datset:TDataSource; flash:TShockwaveFlash);
var i, j : integer;
    filNode, recNode, obNode, arNode, iNode, inNode:IXMLNode;
    xmldoc:TXMLDocument;
begin
try
    //создем Xml документ
    xmldoc:=TXMLDocument.Create(nil);
    xmldoc.Active:=True;

    //Главный тег
    inNode:=xmldoc.AddChild('invoke');
    xmldoc.Encoding:='windows-1251';

    inNode.Attributes['name']:='fromDelphi';        
    iNode:=inNode.AddChild('arguments');    
    arNode:=iNode.AddChild('array');
        
    with datset.DataSet do begin
    {пребираем все записи поля таблыци и взависимосьти от типа поля
    зеркалируем каждое значение в нужные теги}
    first;
        for I := 1 to RecordCount do begin
            obNode:=arNode.AddChild('property');
            obNode.Attributes['id']:=IntToStr(I-1);
            recNode:=obNode.AddChild('object');
            for j := 0 to FieldCount-1  do begin
                filNode:=recNode.AddChild('property');
                filNode.Attributes['id']:=FieldDefs.Items[j].Name;
                
                case Fields[j].DataType of
                    ftString:
                    begin
                        iNode:=filNode.AddChild('string');
                        iNode.Text:=Fields[J].Value;
                    end;
                    ftBoolean:
                    if Fields[J].AsBoolean then
                        iNode:=filNode.AddChild('true')
                    else
                        iNode:=filNode.AddChild('false');
                    ftCurrency:
                    begin
                        iNode:=filNode.AddChild('number');
                        iNode.Text:=FloatToStr(Fields[J].AsFloat);
                    end;
                    ftBCD:
                    begin
                        iNode:=filNode.AddChild('number');
                        iNode.Text:=FloatToStr(Fields[J].AsFloat);
                    end;
                    ftInteger:
                    begin
                        iNode:=filNode.AddChild('number');
                        iNode.Text:=Fields[J].Value;
                    end;
                    ftFloat:
                    begin
                        iNode:=filNode.AddChild('number');
                        iNode.Text:=Fields[J].Value;
                    end;
                    ftDate:
                    begin
                        iNode:=filNode.AddChild('string');
                        iNode.Text:=Fields[J].Value;
                    end;
                    ftDateTime:
                    begin
                        iNode:=filNode.AddChild('string');
                        iNode.Text:=Fields[J].Value;
                    end;
                    ftSmallint:
                    begin
                        iNode:=filNode.AddChild('number');
                        iNode.Text:=Fields[J].Value
                    end;
                    else iNode:=filNode.AddChild('null');
                end;
            end;
            Next;
            end;
        end;
finally
    //Вызаваем фукцию во флешке
    flash.CallFunction(inNode.XML);
    xmldoc:=nil;
end;
end;
Эта процедура будет создаст XML документ в котором будет указанно имя вызываемой функции и ее параметры. Которая будет иметь вид типа. 
<invoke name="fromDelphi">
    <arguments>
        <array>
            <property id="0">
                <object>
                    <property id="MONTH">
                        <string>Январь</string>
                    </property>
                    <property id="DOHOD">
                        <number>1500</number>
                    </property>
                    <property id="RASHOD">
                        <number>700</number>
                    </property>
                    <property id="PRIB">
                        <number>800</number>
                    </property>
                </object>
            </property>
            <property id="1">
                <object>
                    <property id="MONTH">
                        <string>Февраль</string>
                    </property>
                    <property id="DOHOD">
                        <number>2500</number>
                    </property>
                    <property id="RASHOD">
                        <number>1340</number>
                    </property>
                    <property id="PRIB">
                        <number>1160</number>
                    </property>
                </object>
            </property>
            <property id="2">
                <object>
                    <property id="MONTH">
                        <string>Март</string>
                    </property>
                    <property id="DOHOD">
                        <number>1100</number>
                    </property>
                    <property id="RASHOD">
                        <number>820</number>
                    </property>
                    <property id="PRIB">
                        <number>280</number>
                    </property>
                </object>
            </property>
    </arguments>
</invoke>
Пишем в обработчике onCreate нашей формы.
procedure TForm1.FormCreate(Sender: TObject);
begin
//Разделитель точка 
DecimalSeparator:='.';
//Указаваем путь к нашей флешки, здесь она рядом с экзешником
ShockwaveFlash1.LoadMovie(0, ExtractFilePath(Application.ExeName)+'flex.swf');
end;
Бросаем на нашу форму кнопку и вызываем процедуру приготовления XML пакета в котором указанны названия функции нашей флешки и параметры к ней.
procedure TForm1.btn1Click(Sender: TObject);
begin
ClientDataSet.Open;
CdsToXml(DataSet, ShockwaveFlash1);
end;
Компилируем Delphi приложение, находим swf файл скомпилированный на FlashDevelop и кидаем его рядом с экзешником Delphi приложения и запускаем его. Как только флешка загрузится жмем нашу единственную кнопку и радуемся результату :).

Архив с исходниками.

Комментариев нет:

Отправить комментарий