четверг, 26 апреля 2012 г.

Урок 41. Графическое представление информации

В этом уроке мне хочется начать тему, которой я не касался ранее, а именно: рассказать о графическом представлении информации.

Любой бухгалтер скажет: "Мне это зачем? Мне Журнал хозяйственных операций дай и Главную книгу".

Но, во-первых, мы пишем не бухгалтерскую, а очень очень похожую программу.
Во-вторых, у нас уже есть нечто, отдаленно напоминающее ЖХО. Не хватает отчета в удобной форме - сделаем. У нас есть замечательная оборотка, из которой только ленивый не построит Главную книгу.
В-третьих, графическое представление информации удобно в тех случаях, когда нужно быстро проанализировать огромный массив данных, чтобы принять какое-то конструктивное решение.
В-четвертых, интереснее рассказывать о чем-то, о чем еще не рассказывал...

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



Добавляем в проект новую форму (имя формы AnalizFrm), проделывая все те необходимые действия, которые описаны в предыдущем уроке.



Немного слов о том, какие компоненты должны присутствовать на форме:

  • TPopupMenu - понадобится как минимум экспортировать данные,
  • TActionList - коллекция действий (в основном - нажатия кнопок),
  • TPrDOkButton - кнопка из коллекции Pro-Delphi Lib,
  • TPageControl с двумя вкладками, изображенными на рисунках выше,
  • Два элемента управления для ввода дат начала и окончания периода из библиотеки EhLib,
  • TButton - вычислить и показать результат,
  • TADOQuery - обратиться к базе данных с запросом (:-)),
  • TDataSource - связать запрос и отображающий его результат элемент управления на форме, которым является:
  • TDBGridEh - сетка,
  • и наконец - TChart (с вкладки Additional) - компонент, способный отображать графики.
Для подготовки TChart к работе, нужно щелкнуть по нему два раза:




и нажать кнопку "Add", чтобы добавить новую серию. Я выбрал обычную столбчатую диаграмму. Обратите внимание, что  отметка 3D снята (ОК).

Заголовок - title - задайте на одноименной вкладке.



На этом, собственно, предварительная настройка закончена.

Приступим к наполнению кода формы.

Первое, что нам понадобится - массив с константами цветов

var
  AnalizFrm: TAnalizFrm;
  MyColor: array[1..16] of TColor = (
                                    clMaroon
                                    ,clGreen
                                    ,clYellow
                                    ,clOlive
                                    ,clNavy
                                    ,clPurple
                                    ,clTeal
                                    ,clLime
                                    ,ClGray
                                    ,clRed
                                    ,clSilver
                                    ,clBlue
                                    ,ClFuchsia
                                    ,clAqua
                                    ,clMoneyGreen
                                    ,clSkyBlue
                                    );


implementation


Я уже говорил раньше о массивах, но здесь я объявляю его иначе, используя возможность его инициализации при объявлении (т.е. присваиваю значения элементам массива, зная их заранее).

При старте формы логично сделать следующие действия:


procedure TAnalizFrm.FormCreate(Sender: TObject);
begin


  // При создании формы задать значения датам
  Date_N.Value:=Now()-365;        // На год назад
  Date_K.Value:=Now()+1;          // По сегодня включительно


  // Назначить запросу способ подключения
  ADOQuery1.Connection:=MainFrm.ADOConnection1;


end;


При выключении формы - почистить за собой памятийку:


procedure TAnalizFrm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action:= caFree;
end;

Нажатие кнопок (в частности Escape) обработаем следующими двумя процедурами (это - стандартный подход):


procedure TAnalizFrm.FormKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  case Key of            // Start Case


    VK_ESCAPE:
      begin
        MyClose;
      end;


    else


  End;                   // End case
end;


procedure TAnalizFrm.MyClose();
Begin
  Close;
end;



Экшен, подложенный под кнопку "OK":


procedure TAnalizFrm.ButtonClickExecuteExecute(Sender: TObject);
begin


  if (ModalResult=1) OR (ModalResult=2)
  then
  Begin
    MyClose;
  end;


end;


А теперь - самое главное - обработчик нажатия кнопки "Показать"


procedure TAnalizFrm.Button1Click(Sender: TObject);
Var MyStr: String;
    j: Integer;
begin


  // Подготовка запроса
  ADOQuery1.Active:=False;
  ADOQuery1.SQL.Clear;
  MyStr:='SELECT Accounts.ID, Accounts.Name, Sum(Main.Summa) AS MySum ';
  MyStr:=MyStr + 'FROM Accounts INNER JOIN Main ON Accounts.ID = Main.D ';
  MyStr:=MyStr + 'WHERE (((Accounts.Val)='+IntToStr(MySelect.MySel_IDVal)+') AND ((Accounts.Analiz)=True) ';
  MyStr:=MyStr + 'AND ((Main.MyDate)>=#'+FormatDateTime('mm/dd/yyyy',Date_N.Value)+'# And (Main.MyDate)<#'+FormatDateTime('mm/dd/yyyy',Date_K.Value)+'#)) ';
  MyStr:=MyStr + 'GROUP BY Accounts.ID, Accounts.Name ';
  MyStr:=MyStr + 'ORDER BY Sum(Main.Summa) DESC';
  ADOQuery1.SQL.Add(MyStr);
  ADOQuery1.Active:=True;


  // Настройка графика
  j:=1;
  With Series1 do
  begin
   Clear;
    while (Not ADOQuery1.Eof) do
    Begin
      if j>16          // Если количество столбцов в графике больше 16, то цвета будут повторяться
      then
        j:=1
      else
        j:=j+1;


      // Следующей строкой задается отображение очередного столбца
      // Сумма и название (счета) - из запроса
      // Цвет - из массива
      Add(ADOQuery1.FieldByName('MySum').Value, ADOQuery1.FieldByName('Name').Text, MyColor[j]);
      ADOQuery1.Next;
    end;
    Active:=True;
  end;


end;


Секрет  Полезный совет:

если Вы думаете, что я написал все, что вталкивается в переменную MyStr, своими ручками от начала и до конца, то Вы оооооччччееееннньььь ошибаетесь.

Откройте оболочку Access и создайте запрос в режиме конструктора


примерно такой, какой Вам понадобится в программе


Мне понадобится групповая функция (суммирование), выборка из двух связанных таблиц (таблица Accounts даст названия вместо ID), графики я буду строить только по счетам, которые отмечены ИСТИНА в поле Analiz (действительно, зачем мне анализировать затраты, например по перекладываю денег из кошелька в чулок или кубышку?!) и ориентировочно я указал даты (примерно за год).

Если все сделано правильно, то запрос при выполнении отобразит набор записей:


(цифры здесь учебные, не имеющие отношения к реальной жизни  :-) )

А теперь - переведите запрос в режим SQL, нажатием кнопки слева вверху:


- вот он текст нужного запроса! Теперь простым копированием и вставкой (Ctrl-C и Ctrl-V) размещаем его текст в модуле формы, добавляем MyStr, немного функций, которые преобразуют значения из элементов управления (даты) и переменной (MySelect.MySel_IDVal - код валюты) в удобоваримые для Delphi и все.



Обращаю Ваше внимание на то, как я обошелся с датами в запросе:
Access "любит" дату в запросе вот в таком виде

#mm/dd/yyyy#

Если Вы будете использовать базу данных на основе SQL Server, то диезы Вам точно не понадобятся. Но, поскольку я здесь использую БД Access, нужно привыкнуть к этому формату. Кроме того, настоятельно рекомендую изменить системные настройки, указав в качестве разделителя в дате "/" вместо установленной по умолчанию точки. Мой многолетний опыт общения с оболочкой Access настаивает на том, что не все и не всегда запросы к базе данных в среде Access возвращают корректный (правильный) набор данных при использовании в качестве разделителя даты точки!

* * *

Пункт выпадающего меню "Экспорт".  Прежде нужно назначить компоненту TChart свойство PopupMenu=PopupMenu1, а затем написать обработчик выбора пункта меню:

procedure TAnalizFrm.N_ExportClick(Sender: TObject);
begin


  // Для вывода в Excel используется имеющаяся в модуле главной формы процедура
  MainFrm.ExportTo(PChar('Лист 1'), DBGridEh1, MainFrm.XLApp, PChar('Затраты'), PChar('Период: '+FormatDateTime('dd/mm/yy',Date_N.Value)+' - '+FormatDateTime('dd/mm/yy',Date_K.Value)));


end;

В результате работы получим вот такую табличку:




* * *

В главной форме необходимо реализовать вывод формы "Анализ затрат" на экран по выбору пункта Меню - Отчетные формы - Анализ затрат (все)


procedure TMainFrm.N_AnalizClick(Sender: TObject);
begin


  // Анализ затрат
  Application.CreateForm(TAnalizFrm, AnalizFrm);
  AnalizFrm.ShowModal;
  AnalizFrm.Free;


end;

чтобы получить в итоге вот такую картинку:


глянув на которую, можно впасть в кому... :-)


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

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