уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.
Сегодня мы рассмотрим один из наиболее интересных, на мой взгляд, моментов работы с Excel в Delphi — построение диаграмм.
Забегая немного вперед, скажу, что есть несколько способов добавления диаграммы в рабочую книгу Excel. Чтобы все статьи по вопросам автоматизации Excel в блоге были как-то логически связаны, я решил сегодня рассмотреть способ добавления диаграммы через объект ChartObjects, с которым мы встречались, когда разбирали методы объекта WorkSheet.
План статьи:
- Копируем данные из таблицы и оформление StringGrid
- Добавление и редактирование диаграммы Excel
Итак, поставим перед собой цель — построить простой линейный график на основании данных таблицы (StringGrid) нашего приложения. При этом, чтобы продолжить предыдущую тему, постараемся скопировать таблицу на лист один-к-одному.
1. Копируем данные из таблицы и оформление StringGrid.
Для копирования данных из таблицы на лист Excel воспользуемся простейшей процедурой, которую мы уже с Вами рассматривали. Для наглядности, приведу листинг процедуры ещё раз:
procedure WriteTable(FirstCol, FirstRow:integer; Grid: TStringGrid); var col,row:integer; begin try for col := 0 to Grid.ColCount - 1 do for row := 0 to Grid.RowCount - 1 do MyExcel.ActiveWorkBook.ActiveSheet.Cells[FirstRow+row, FirstCol+col]:=Grid.Cells[col, row]; except raise Exception.Create('Запись таблицы завершилась ошибкой') end; end;
Теперь начнем копировать оформление. Во-первых, необходимо определить свойство BorderStyle у StringGrid — оно может быть либо bsSingle либо bsNone. В первом случае внешние границы таблицы будут выделяться. Отсюда следует, что и наша таблица в Excel должна иметь окантовку. Делается это просто:
... if Grid.BorderStyle=bsSingle then begin //отрисовываем внешние границы сплошной линией Range1.Borders[xlEdgeBottom].LineStyle:=xlContinuous; Range1.Borders[xlEdgeTop].LineStyle:=xlContinuous; Range1.Borders[xlEdgeLeft].LineStyle:=xlContinuous; Range1.Borders[xlEdgeRight].LineStyle:=xlContinuous; end; ...
Во-вторых, StringGrid может быть с отрисованными внутренними линиями и без них. За отрисовку внутренних линий отвечают два параметра из свойства Options у StringGrid: goFixedVertLine (прорисовка вертикальных линий в ) и goFixedHorzLine (прорисовка горизонтальных линий в StringGrid).
Проверяем наш StringGrid и, в случае необходимости, прорисовываем внутренние границы ячеек:
if goFixedVertLine in Grid.Options then Range1.Borders[xlInsideVertical].LineStyle:=xlSolid; if goFixedHorzLine in Grid.Options then Range1.Borders[xlInsideHorizontal].LineStyle:=xlContinuous;
А теперь самое интересное — определение цветов StringGrid и перенос их в таблицу Excel. Для того, чтобы перевести цвет в Delphi в цвет, приемлемый для Excel, напишем небольшую подпрограмму:
R := GetRValue(ColorToRGB(Color)); G := GetGValue(ColorToRGB(Color)); B := GetBValue(ColorToRGB(Color));
где Color — это любой из цветов в Delphi, например clRed или clBtnFace. В итоге мы получим три составляющих для RGB, который допускается использовать при заливке ячеек в Excel.
Теперь можно копировать цвета фиксированных ячеек StringGrid:
Cell1:=MyExcel.ActiveWorkBook.ActiveSheet.Cells[FirstRow, FirstCol]; Cell2:=MyExcel.ActiveWorkBook.ActiveSheet.Cells[FirstRow+Grid.RowCount-1, FirstCol+Grid.ColCount-1]; //выделяем занятую таблицей область листа Range1:=MyExcel.ActiveWorkBook.ActiveSheet.Range[Cell1, Cell2]; if Grid.FixedCols>0 then //есть фиксированные колонки for I:=1 to Grid.FixedCols do Range1.Columns[i].Interior.Color:=RGB(r,g,b); if Grid.FixedRows>0 then //есть фиксированные строки for I:=1 to Grid.FixedRows do Range1.Rows[i].Interior.Color:=RGB(r,g,b);
Таким образом мы скопировали наш StringGrid на лист Excel. Конечно, здесь есть свои недостатки, например StringGrid может быть раскрашен как новогодняя ёлка или иметь совершенно иное оформление, чем стандартное и тогда, следуя вышеперечисленным операциям Вы не добьетесь копирования оформления один-к-одному. Но, при небольшом дополнении исходного кода этого можно легко добиться — суть вопроса остается той же, как и набор операций работы Delphi с Excel.
2. Добавление и редактирование диаграммы Excel
Теперь, имея в своем распоряжении данные, можно приступать к построению диаграммы Excel с помощью Delphi.
Для того, чтобы добавить в коллекцию ChartObjects новый объект необходимо выполнить метод Add:
ChartObjects.Add(Left, Top, Width, Height)
где:
Left и Top — начальные координаты нового объекта (в пикселях), относительно левого верхнего угла ячейки A1 на листе или в левом верхнем углу графика.
Width и Height — соответственно ширина и высота новой диаграммы.
В результате выполнения метода в коллекцию ChartObjects добавляется новый объект. Пока никаких данных объект не использует. По сути в добавляется пустой холст диаграмм Excel.
Для того, чтобы построить диаграмму, необходимо:
- получить ссылку на объект Chart из коллекции ChartObjects;
- воспользоваться методом ChartWizard
Чтобы получить ссылку на вновь добавленный объект необходимо выполнить следующую операцию:
var Chart: OLEVariant; ChartCount: integer; begin ... ChartCount:=MyExcel.ActiveWorkBook.ActiveSheet.ChartObjects.Count; Chart:=MyExcel.ActiveWorkBook.ActiveSheet.ChartObjects[ChartCount].Chart; ... end;
Метод ChartWizard содержит следующие параметры:
ChartWizard(Source, Gallery, Format, PlotBy, CategoryLabels, SeriesLabels, HasLegend, Title, CategoryTitle, ValueTitle, ExtraTitle)
Параметр | Тип | Описание |
Source | Variant | диапазон, который содержит исходные данные для нового графика |
Gallery | integer | (Enumerations xlChartType) — тип диаграммы. Для метода ChartWizard может принимать следующие значения: xlArea, xlBar, xlColumn, xlLine, xlPie, xlRadar, xlXYScatter, xlCombination, xl3DArea, xl3DBar, xl3DColumn, xl3DLine, xl3DPie, xl3DSurface, xlDoughnut, xlDefaultAutoFormat |
Format | integer | (1..10) — может быть числом от 1 до 10, в зависимости от типов галереи. Если этот аргумент опущен, Microsoft Excel выбирает значение по умолчанию в зависимости от типа диаграммы и источника данных. Например Format = 5 для нашего случая заставит Excel прорисовать на диаграмме линии сетки. |
PlotBy | определяет каким образом данные располагаются в Source. Может принимать два значения xlColumns = 2 (данные расположены в столбцах) xlRows = 1 (данные расположены в строках) | |
CategoryLabel | integer | определяет номер строки или столбца в пределах источника, содержащим метку категории. Допустимые значения от 0 (ноль) до предпоследнего номера столбца или строки источника |
SeriesLabels | integer | определяет номер строки или столбца в пределах источника, содержащим метку набора данных |
HasLegend | boolean | определяет будет ли на диаграмме Excel отражена легенда |
Title | string | заголовок диаграммы Excel |
CategoryTitle | string | подпись оси категорий |
ValueTitle | string | подпись оси значений |
ExtraTitle | string | дополнительная подпись оси при построении трехмерных графиков |
Как и для любых других методов Excel, в Delphi допускается опускать некоторые параметры или отмечать их как EmptyParam.
Теперь построим наш график. Для этого я написал небольшую процедуру:
procedure AddLineChartFromTable(X, Y, Height, Width, Format: integer; Title, XLabel,YLabel: string; DataGrid: TStringGrid; DataPosition: byte; ChartType:integer); var Chart: OLEVariant; DataRange: OLEVariant; begin //Вставляем данные из таблицы WriteTable(1, 1, DataGrid); //выбираем данные для диаграммы DataRange:=MyExcel.ActiveWorkBook.ActiveSheet.UsedRange; //добавляем новую диаграмму на активный лист MyExcel.ActiveWorkBook.ActiveSheet.ChartObjects.Add(x,y,width,height); //выбираем последнюю добавленную диаграмму Chart:=MyExcel.ActiveWorkBook.ActiveSheet.ChartObjects[MyExcel.ActiveWorkBook.ActiveSheet.ChartObjects.Count]; Chart.Chart.ChartWizard(Source:=DataRange, Gallery:=xlLine, Format:=Format, PlotBy:=DataPosition, CategoryLabels:=1, SeriesLabels:=1, HasLegend:=true, Title:=Title, CategoryTitle:=XLabel, ValueTitle:=YLabel); end;
Как видите, все достаточно просто. Берется таблица StringGrid, данные из неё переносятся в Excel на активный лист и, затем, эти данные используются для построения графика. Причём первая ячейка таблицы используется для подписи рядов данных. В результате выполнения процедуры я получил следующую диаграмму Excel:
Ну, и наконец, для того, чтобы представить этот же график в объемном виде, воспользуемся одним из многочисленных свойств объекта Chart — ChartType:
Chart.ChartType:=xl3DLine;
в итоге получим следующий вид диаграммы:
На сегодня все В следующий раз займемся свойствами объекта Chart, научимся строить различные типы диаграмм и изменять область построения диаграммы. А пока можете поэкспериментировать с параметрами у ChartWizard и посмотреть какие ещё виды диаграмм Excel Вы сможете построить в Delphi.
Книжная полка
Название:Разработка приложений Microsoft Office 2007 в Delphi Описание Описаны общие подходы к программированию приложений MS Office. Даны программные методы реализации функций MS Excel, MS Word, MS Access и MS Outlook в среде Delphi. |
0
0
голоса
Рейтинг статьи
уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.
Поскольку этот вопрос перекликается с предыдущим в плане навыков программной работы с приложениями MSOffice (да и вообще с COM-объектами), попробую объяснить порядок составления своего Delphi-кода на основе VBA. Напоследок отложим непосредственно ответ на сам вопрос.
Во-первых, нужно представлять себе иерархию объектов MS Office-приложения. В Excel она несколько проще, чем в Word.
Основное:
- Application - глобальный объект, из него можно "достучаться" до чего угодно
- Workbooks - коллекция книг,
(Workbook) - каждая из которых содержит
- Worksheets - коллекцию рабочих листов
(Worksheet) - на которых мы и размещаем информацию.
- Cells - ячейки листа. Именно с ними и производится бОльшая часть работы -
размещение данных, объединение/разделение, изменение шрифта и т.п.
Собственно, по большому счету все. Остальное запоминать не обязательно, потому что мощным помощником выступает сам Excel/Word, позволяя записывать все «ручные» действия пользователя в макросы.
Приступим. Первый этап — размещение данных на нужном листе. Здесь пользуемся приведенной ранее иерархией:
var
Excel, Book, Sheet:OleVariant;
i:Integer;
begin
Excel:=CreateOleObject('Excel.Application');
Excel.Visible:=True;
Book:=Excel.Workbooks.Add;
Sheet:=Book.Worksheets[1];
for i := 1 to 5 do
begin
Sheet.Cells[i, 1].Formula:=i;
Sheet.Cells[i, 2].Formula:=Sin(i);
end;
Замечательно то, что большинство свойств принимает тип Variant, то есть — практически любые значения: строки, integer, Double и т.п.
Есть несколько способов задать значение ячейке: Formula
, FormulaR1C1
, Value
, etc. Нужно понимать их различия и пользоваться необходимыми соответственно задаче. Чаще всего используются именно Formula
и FormulaR1C1
Так же полезно понимать, что каждая ячейка на самом деле это объект Range, то есть представляет собой любое, произвольное количество ячеек и работа с несколькими ячейками из состава Range
не отличается от работы с одной ячейкой, полученной через Cells
.
Далее необходимо на лист добавить график. Мы не знаем, как это сделать, да и из-за однократности задачи помнить, где этот график находится в иерархии объектов — расточительно.
Заходим в меню «Вид», нажимаем «Макросы — Запись макроса» и нажимаем «ОК». Переходим на вкладку «Вставка» и добавляем простую гистограмму. Далее выбираем дипазон данных (я взял B1-B5
), изменяем подписи (выбрал A1-A5
) подтверждаем изменения и останавливаем запись макроса («Вид» — «Макросы» — «Остановить запись»). Теперь осталось войти внутрь макроса («Вид» — «Макросы» — «Макросы»-«Изменить»). Должен получиться примерно такой код VBA:
ActiveSheet.Shapes.AddChart.Select
ActiveChart.ChartType = xlColumnClustered
ActiveChart.SetSourceData Source:=Range("B1:B5")
ActiveChart.SeriesCollection(1).XValues = "=Лист1!$A$1:$A$5"
И вот тут нам понадобится справка, потому что дальше не все так тривиально. Разберем по строчкам:
ActiveSheet.Shapes.AddChart.Select
— последний метод (Select
) нас не интересует, поскольку он просто выделяет добавленную фигуру на листе. Для программной работы выделение объекта излишне. А вот AddChart
добавляет объект Shape
, а нам нужен объект Chart. VBA дальше идет по пути наименьшего сопротивления — использует свойство Application.ActiveChart (ActiveChart.ChartType = xlColumnClustered
и т.п.). Но нам это не подходит, поскольку (повторюсь) при программной работе с Excel нежелательно использовать «активные элементы», нужно работать именно с тем, что мы добавили. Выясняем, что у объекта Shape есть свойство Chart, именно оно нам нужно. Соответственно, всю дальнейшую работу строим именно на этом, переводя код VBA в Delphi:
Shape:=Sheet.Shapes.AddChart; // повторюсь - Select нам не нужен.
// но при желании потом можно сделать Shape.Select
Shape.Chart.ChartType := xlColumnClustered; // вместо ActiveChart - наш Shape.Chart
Shape.Chart.SetSourceData(Source:=Sheet.Range['B1:B5']);
Shape.Chart.SeriesCollection(1).XValues := Format('=%s!$A$1:$A$5', [Sheet.Name]);
Здесь следует обратить внимание, что мы избегаем называть возможно локализованное имя листа (оно может зависеть от языка Office, пользователь может его переименовать и т.п.). Поэтому берем реальное наименование из свойства Sheet.Name.
Вот и все. Полный получившийся код:
procedure TForm8.btn2Click(Sender: TObject);
var
Excel, Book, Sheet, Shape:OleVariant;
i:Integer;
begin
Excel:=CreateOleObject('Excel.Application');
Excel.Visible:=True;
Book:=Excel.Workbooks.Add;
Sheet:=Book.Worksheets[1];
for i := 1 to 5 do
begin
Sheet.Cells[i, 1].FormulaR1C1:=i;
Sheet.Cells[i, 2].FormulaR1C1:=Sin(i);
end;
Shape:=Sheet.Shapes.AddChart;
Shape.Chart.ChartType := xlColumnClustered;
Shape.Chart.SetSourceData(Source:=Sheet.Range['B1:B5']);
Shape.Chart.SeriesCollection(1).XValues := Format('=%s!$A$1:$A$5', [Sheet.Name]);
end;
В заключение замечание по поводу используемых констант xlColumnClustered
и подобных: можно подключить в uses модуль Excel2010
, ExcelXP
или другой, имеющийся в вашей версии Delphi. Но можно и подставлять значения констант вручную, через поиск в Google (лучше брать ссылки на первоисточник — microsoft). Например, поиск по xlColumnClustered
первой же ссылкой дает перечень констант XlChartType Enumeration
← →
agisland ©
(2005-04-26 01:23)
[0]
Как нарисовать из делфи диаграмму в excel
var
Excel,Book,List,Value,Chart:Variant;
LSID:Integer;
begin
try
Excel:=GetActiveOleObject(«Excel.Application»);
except
Excel := CreateOleObject(«Excel.Application»);
end;
LSID:=GetUserDefaultLCID;
Book:=Excel.WorkBooks.Add();
List:=Book.WorkSheets.Add();
а дальше… Допустим диаграмма нужна по ячейкам a1:a5, b1:b5
← →
Ольга
(2005-04-26 07:48)
[1]
// создание диаграммы
Book.Charts.Add;
// название диаграммы
Book.ActiveChart.HasTitle := True;
Book.ActiveChart.ChartTitle.Characters.Text := Title;
// тип диаграммы
Book.ActiveChart.ChartType:= xlLineStacked;
// источник данных
Range1:= Sheet.Range[Cells[r1, c1], Cells[r1, c2]];
Range2:= Sheet.Range[Cells[r2, c3], Cells[r2, c4]];
Book.ActiveChart.SetSourceData(Range1, PlotBy);
Book.ActiveChart.SeriesCollection(1).XValues := Range2;
PlotBy:= xlColumns; // по колонкам
PlotBy:= xlRows; // по рядам
// расположение диаграммы в книге
Book.ActiveChart.Location(xlLocationAsNewSheet, NameSheet); // на новом листе
Book.ActiveChart.Location(xlLocationAsObject, NameSheet); // на листе NameSheet
// ось абсцисс (значений)
Book.ActiveChart.HasAxis(xlValue, xlPrimary) := True;
// отображение основных линий на диаграмме
Book.ActiveChart.Axes(xlValue).HasMajorGridlines := True;
// отображение промежуточных линий на диаграмме
Book.ActiveChart.Axes(xlValue).HasMinorGridlines := False;
// заголовок оси
Book.ActiveChart.Axes(xlValue).HasTitle := True;
Book.ActiveChart.Axes(xlValue).AxisTitle.Characters.Text := TitleY;
// ось ординат (категорий)
Book.ActiveChart.HasAxis(xlCategory, xlPrimary) := True;
// отображение основных линий на диаграмме
Book.ActiveChart.Axes(xlCategory).HasMajorGridlines := True;
// отображение промежуточных линий на диаграмме
Book.ActiveChart.Axes(xlCategory).HasMinorGridlines := False;
// заголовок оси
Book.ActiveChart.Axes(xlCategory, xlPrimary).HasTitle := True;
Book.ActiveChart.Axes(xlCategory, xlPrimary).AxisTitle.Characters.Text := TitleX;
// добавить новую серию на диаграмму
Range3:= Sheet.Range[Cells[r3, c1], Cells[r3, c4]];
NewSeries:=Book.ActiveChart.SeriesCollection.Add(Range3); // диапазон значений
Book.ActiveChart.SeriesCollection[1].XValues:= Range2; // диапазон категорий
NewSeries:=Book.ActiveChart.SeriesCollection[Book.ActiveChart.SeriesCollection.Count];
NewSeries.Name:=Caption; // заголовок серии
NewSeries.Border.Color:=SeriesColor; // цвет
NewSeries.MarkerBackgroundColor:=SeriesColor;
NewSeries.MarkerForegroundColor:=SeriesColor;
NewSeries.Smooth:= True; // сглаживание графика
← →
КиТаЯц ©
(2005-05-14 12:03)
[2]
>Ольга (26.04.05 07:48) [1]
Круто!
Может кто знает как передать в Excel готовую tDBChart???
Ну, типа as image…
← →
andrey__
(2005-05-16 16:12)
[3]
>agisland © (26.04.05 01:23)
Если у тебя получилось или у кого-то ещё то приведите полный пример. А то у меня на строке: Book.ActiveChart.HasTitle := True;
пишет «Нельзя установить свойство HasTitle класса Chart.»
← →
HeadLess
(2005-05-17 14:35)
[4]
А не проще ли подготовить макросы постороения диаграммы, а потом их просто вызывать из программы строчкой типа :
var
Excel,Book,List,Value,Chart:Variant;
…
…
Excel.run(«My_Chart_Macro»);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
uses ... ComObj, ActiveX; //... Procedure TCalcOfDefl.RWExcel; var SIR: boolean; APID: String; App, Ch, Range: Variant; i, j: integer; begin // Указать программный идентификатор приложения-сервера APID := 'Excel.Application'; SIR := False; // Создать один экземпляр сервера App := CreateOleObject(APID); SIR := True; // показать окно приложения на экране App.Visible := True; // Редактируем документ App.WorkBooks.Add; // ... // данные для диаграмм: Range := App.WorkBooks[1].WorkSheets[1].Range['D:D']; // добавляем новыю диаграмму на активный лист App.ActiveWorkBook.ActiveSheet.ChartObjects.Add(100, 10, 500, 400); // выбираем последнюю добавленную диаграмму Ch := App.WorkBooks[1].WorkSheets[1].ChartObjects [App.WorkBooks[1].WorkSheets[1].ChartObjects.Count]; Ch.Chart.ChartWizard(Source := Range, Gallery := xlLine{Вот здесь всегда ошибка, если оставить так, то E2003 Undeclared identifier: 'xlLine'; если взять в любые скобки ругается, если апострофы поставить:'xlLine' то компилируется но выдает ошибку Method 'ChartWizard' not supported by automation object; если использовать любое значение: xlArea, xlBar, xlColumn, xlLine, xlPie, xlRadar, xlXYScatter, xlCombination, xl3DArea, xl3DBar, xl3DColumn, xl3DLine, xl3DPie, xl3DSurface, xlDoughnut, xlDefaultAutoFormat всеравно всегда ошибка}, Format := 5, PlotBy := 2, CategoryLabels := 1, SeriesLabels := 1, HasLegend := False, Title := 'График зависимости прогиба от угла', CategoryTitle := 'f', ValueTitle := 'φ'); if not SIR then App.Quit; App := Unassigned; end; |