Интерактивные отчеты позволяют определить в отчёте действие, которое произойдет при щелчке мыши на каком-нибудь объекте отчета в окне предварительного просмотра. В предыдущих версиях FastReport существовало несколько способов сделать отчет интерактивным.
Язык программирования самого высокого уровня содержит всего несколько команд для управления программистами
21 апреля 2014
11 апреля 2014
FastReport. PDF-экспорт и встраивание шрифта
В списке новшеств FastReport VCL 5 значится "В PDF-экспорт улучшено встраивание шрифтов: вместо целого шрифта встраивается только используемое в отчёте подмножество символов шрифтов. Это позволяет значительно сократить размер файла". В своих программах я часто работаю с документами в PDF-формате, поэтому вопрос размера документа, экспортированного из FastReport в PDF-файл меня интересует.
04 апреля 2014
FastReport VCL 5. Мы уже не ждали, а он припёрся
Вчера вечером, разгребая спам, я наткнулся на письмо от FastReports, Inc с темой "Внезапно релиз FastReport VCL 5". О как! Мы уже и не ждали, а он припёрся. Тут же в голове возникла знаменитая фраза, которую сказал дедушка Ленин 7 ноября 1917 года: "Товарищи! Рабочая и крестьянская революция, о необходимости которой всё время говорили большевики, свершилась! ". Спросите: "Почему такая реакция? ". Для тех, кто не в теме расскажу предысторию этого события.
18 июня 2012
Хранение массива в BLOB-поле - версия 2
Комментарий Алексея Тимохина к моей заметке "Хранение массива в BLOB-поле" навел меня на мысль, что приведенный мной код можно значительно упростить, если выбросить из него использование TMemoryStream для временного хранения информации.
Что бы загрузить массив в параметр query типа ftBlob можно использовать метод SetBlobData:
qInsertResearchData.Params[5].SetBlobData(aData, Length(aData) * SizeOf(aData[0]))
Чтение массива из поля типа TBlobField тоже можно записать коротко:
// Установка размера динамического массива aData
SetLength(aData, qResearchDATA.BlobSize div SizeOf(aData[0]));
// Запись содержимого поля qResearchDATA типа TBlobField в массив aData
qResearch.GetBlobFieldData(qResearchDATA.FieldNo, TBlobByteData(aData))
Т.к. размер массива я устанавливаю равным размеру содержимого BLOB-поля, то для исключения лишних проверок в GetBlobFieldData я заменил вызов этого метода на свой код и теперь запись содержимого поля qResearchDATA типа TBlobField в массив aData выглядит так:
With qResearch.CreateBlobStream(qResearchDATA, bmRead) do
Try
ReadBuffer(aData[0], qResearchDATA.BlobSize);
Finally
Free;
End;
Что бы загрузить массив в параметр query типа ftBlob можно использовать метод SetBlobData:
qInsertResearchData.Params[5].SetBlobData(aData, Length(aData) * SizeOf(aData[0]))
Чтение массива из поля типа TBlobField тоже можно записать коротко:
// Установка размера динамического массива aData
SetLength(aData, qResearchDATA.BlobSize div SizeOf(aData[0]));
// Запись содержимого поля qResearchDATA типа TBlobField в массив aData
qResearch.GetBlobFieldData(qResearchDATA.FieldNo, TBlobByteData(aData))
Т.к. размер массива я устанавливаю равным размеру содержимого BLOB-поля, то для исключения лишних проверок в GetBlobFieldData я заменил вызов этого метода на свой код и теперь запись содержимого поля qResearchDATA типа TBlobField в массив aData выглядит так:
With qResearch.CreateBlobStream(qResearchDATA, bmRead) do
Try
ReadBuffer(aData[0], qResearchDATA.BlobSize);
Finally
Free;
End;
11 июня 2012
Хранение массива в BLOB-поле
Понадобилось мне хранить результаты измерений в базе данных. Просто сохранить их в таблицу не получится, т.к. количество строк с информацией в одном измерении быстро перевалит за миллион, и при сохранении их в одной транзакции пользователь успеет попить кофе. Плодить множество мелких транзакций по несколько тысяч записей тоже не сильно прибавит скорости. Скорость при последующей вычитке из базы данных миллионов строк тоже будет печальной. Поэтому единственный очевидный для меня выход - это сохранить результаты измерения в поле типа BLOB. Измерения у меня хранились в многомерном массиве, но я упрощу код и объявлю динамический одномерный массив целых чисел:
Var
aData: Array of Integer;
Процедура записи динамического массива в базу данных получилась примерно такая:
Var
ms: TMemoryStream;
...
begin
...
Try
ms := TMemoryStream.Create;
// Запись содержимого массива aData в поток ms
ms.WriteBuffer(aData[0], Length(aData) * SizeOf(aData[0]));
// Запись содержимого потока ms в параметр query для вставки данных
qInsertResearch.Params[5].LoadFromStream(ms, ftBlob);
Finally
ms.Free;
End;
...
А процедура чтения массива из базы данных не сложнее процедуры записи:
Var
ms: TMemoryStream;
...
begin
...
Try
ms := TMemoryStream.Create;
// Запись содержимого поля qResearchDATA типа TBlobField в поток ms
qResearchDATA.SaveToStream(ms);
// Установка размера динамического массива aData
SetLength(aData, ms.Size div SizeOf(aData[0]));
ms.Position := 0;
// Запись содержимого потока ms в массив aData
ms.ReadBuffer(aData[0], ms.Size);
Finally
ms.Free;
End;
...
Теперь на запись/чтение нескольких миллионов записей моя программа тратит секунду. Вместо размера первого элемента массива SizeOf(aData[0]) вы можете использовать размер типа элементов массива (для приведенного мной примера - SizeOf(Integer)). Но такой вариант кода будет менее универсальным, т.к. будет зависеть от используемого типа данных.
P.S. При использовании такого способа хранения данных не забывайте, что размер некоторых типов данных зависит от операционной системы, для которой скомпилирована программа. Иначе информация, которую прочтет из базы данных ваша 64-битная программа перекомпилированная из 32-битной, вас неприятно удивит.
Var
aData: Array of Integer;
Процедура записи динамического массива в базу данных получилась примерно такая:
Var
ms: TMemoryStream;
...
begin
...
Try
ms := TMemoryStream.Create;
// Запись содержимого массива aData в поток ms
ms.WriteBuffer(aData[0], Length(aData) * SizeOf(aData[0]));
// Запись содержимого потока ms в параметр query для вставки данных
qInsertResearch.Params[5].LoadFromStream(ms, ftBlob);
Finally
ms.Free;
End;
...
А процедура чтения массива из базы данных не сложнее процедуры записи:
Var
ms: TMemoryStream;
...
begin
...
Try
ms := TMemoryStream.Create;
// Запись содержимого поля qResearchDATA типа TBlobField в поток ms
qResearchDATA.SaveToStream(ms);
// Установка размера динамического массива aData
SetLength(aData, ms.Size div SizeOf(aData[0]));
ms.Position := 0;
// Запись содержимого потока ms в массив aData
ms.ReadBuffer(aData[0], ms.Size);
Finally
ms.Free;
End;
...
Теперь на запись/чтение нескольких миллионов записей моя программа тратит секунду. Вместо размера первого элемента массива SizeOf(aData[0]) вы можете использовать размер типа элементов массива (для приведенного мной примера - SizeOf(Integer)). Но такой вариант кода будет менее универсальным, т.к. будет зависеть от используемого типа данных.
P.S. При использовании такого способа хранения данных не забывайте, что размер некоторых типов данных зависит от операционной системы, для которой скомпилирована программа. Иначе информация, которую прочтет из базы данных ваша 64-битная программа перекомпилированная из 32-битной, вас неприятно удивит.
Продолжение...
14 октября 2011
О нумерации версий Delphi
История развивается по спирали, а пони бегает по кругу... |
Пятничные размышления, на которые натолкнуло письмо одного моего приятеля.
Сначала разработчики Delphi нумеровали версии просто: 1, 2, 3, 4, 5, 6, 7, 8. Затем, с подачи маркетологов Microsoft перешли на нумерацию версий по годам: 2005, 2006, 2007, 2009, 2010. В 2010 году Embarcadero придумала для всех своих продуктов новый бренд - "XE". "XE" - это не "Express Edition" как у Oracle, а два понятия: "X" - гетерогенность платформы (например, доступ к различным базам данных без дополнительных затрат) и "E" - скромное "Embarcadero". Поэтому с 2010 года началась новая эра в нумерации версий Delphi: Delphi XE, Delphi XE2... Что будет дальше? А дальше:
Сначала разработчики Delphi нумеровали версии просто: 1, 2, 3, 4, 5, 6, 7, 8. Затем, с подачи маркетологов Microsoft перешли на нумерацию версий по годам: 2005, 2006, 2007, 2009, 2010. В 2010 году Embarcadero придумала для всех своих продуктов новый бренд - "XE". "XE" - это не "Express Edition" как у Oracle, а два понятия: "X" - гетерогенность платформы (например, доступ к различным базам данных без дополнительных затрат) и "E" - скромное "Embarcadero". Поэтому с 2010 года началась новая эра в нумерации версий Delphi: Delphi XE, Delphi XE2... Что будет дальше? А дальше:
- Delphi XE3
- Delphi XE4
- Delphi XE5
- Delphi XE6
- Delphi XE7
- Delphi XE8
- Delphi XE9
- Delphi XEA
- Delphi XEB
- Delphi XEC
- Delphi XED
- Delphi XEE
15 сентября 2011
Конвертирование XLS/XLSX/XLSM в XLSB
С появлением MS Excel 2007 на смену привычного XLS-файла пришли сразу три формата:
Ни одна из библиотек для работы с файлами Excel в Delphi не поддерживает XLSB-формат. Например, XLSReadWrite поддерживает только XLSX, а авторы TMS FlexCel вообще не могут сказать, когда у них будет поддержка формата Excel 2007 и будет ли вообще. Поэтому, что бы конвертировать файлы Excel в XLSB-формат необходимо использовать OLE:
Var
xls: OleVariant;
...
Const
// formats in Excel 2007-2010
xlExcel12 = 50; // XLSB
xlOpenXMLWorkbook = 51; // XLSX
xlOpenXMLWorkbookMacroEnabled = 52; // XLSM
xlExcel8 = 56; // XLS (export to Excel 97-2003)
...
Procedure XYZ.ConvertFilesToXLSB(slFiles: TStringList);
Var
iFile: Integer;
begin
Try
Try
xls := CreateOleObject('Excel.Application');
xls.DisplayAlerts := False;
If StrToFloat(StringReplace(xls.Version, '.', DecimalSeparator, [])) < 12
then WriteToLog('Error! Requires Excel 2007/2010')
else for iFile := 0 to slFiles.Count-1 do
ConvertToXLSB(slFiles[iFile]);
Finally
xls.Quit;
xls := UnAssigned;
End;
Except
on E: Exception do
WriteToLog(E.Message);
End;
end;
Где ConvertToXLSB:
Procedure XYZ.ConvertToXLSB(sFileName: String);
Var
sFileNameTo: String;
begin
sFileName := ExpandFileName(sFileName);
sFileNameTo := ChangeFileExt(sFileName, '.xlsb');
Try
Try
xls.Workbooks.Open(sFileName);
xls.ActiveWorkbook.SaveAs(Filename := sFileNameTo, FileFormat := xlExcel12);
Finally
xls.Workbooks.Close;
End;
Except
on E: Exception do
WriteToLog(E.Message);
End;
end;
Думаю, что код простой и комментировать нечего. Остановлюсь только на строке проверки версии MS Excel. xls.Version возвращает номер версии MS Excel в виде строки, где цифры разделены точкой (например, "11.0" для Excel 2003, "12.0" для Excel 2007...), поэтому, чтобы получить номер версии в виде числа его необходимо преобразовать следующим способом:
StrToFloat(StringReplace(xls.Version, '.', DecimalSeparator, []))
- XLSX - стандартный формат файлов Excel 2007-2010 на основе XML;
- XLSM - формат Excel 2007-2010 на основе XML с поддержкой макросов (в отличие от XLSX он позволяет сохранять код макросов MS Visual Basic для приложений (VBA) и листы макросов MS Excel 4.0 (XLM));
- XLSB - формат двоичных файлов Excel 2007-2010 (BIFF12).
Ни одна из библиотек для работы с файлами Excel в Delphi не поддерживает XLSB-формат. Например, XLSReadWrite поддерживает только XLSX, а авторы TMS FlexCel вообще не могут сказать, когда у них будет поддержка формата Excel 2007 и будет ли вообще. Поэтому, что бы конвертировать файлы Excel в XLSB-формат необходимо использовать OLE:
Var
xls: OleVariant;
...
Const
// formats in Excel 2007-2010
xlExcel12 = 50; // XLSB
xlOpenXMLWorkbook = 51; // XLSX
xlOpenXMLWorkbookMacroEnabled = 52; // XLSM
xlExcel8 = 56; // XLS (export to Excel 97-2003)
...
Procedure XYZ.ConvertFilesToXLSB(slFiles: TStringList);
Var
iFile: Integer;
begin
Try
Try
xls := CreateOleObject('Excel.Application');
xls.DisplayAlerts := False;
If StrToFloat(StringReplace(xls.Version, '.', DecimalSeparator, [])) < 12
then WriteToLog('Error! Requires Excel 2007/2010')
else for iFile := 0 to slFiles.Count-1 do
ConvertToXLSB(slFiles[iFile]);
Finally
xls.Quit;
xls := UnAssigned;
End;
Except
on E: Exception do
WriteToLog(E.Message);
End;
end;
Где ConvertToXLSB:
Procedure XYZ.ConvertToXLSB(sFileName: String);
Var
sFileNameTo: String;
begin
sFileName := ExpandFileName(sFileName);
sFileNameTo := ChangeFileExt(sFileName, '.xlsb');
Try
Try
xls.Workbooks.Open(sFileName);
xls.ActiveWorkbook.SaveAs(Filename := sFileNameTo, FileFormat := xlExcel12);
Finally
xls.Workbooks.Close;
End;
Except
on E: Exception do
WriteToLog(E.Message);
End;
end;
Думаю, что код простой и комментировать нечего. Остановлюсь только на строке проверки версии MS Excel. xls.Version возвращает номер версии MS Excel в виде строки, где цифры разделены точкой (например, "11.0" для Excel 2003, "12.0" для Excel 2007...), поэтому, чтобы получить номер версии в виде числа его необходимо преобразовать следующим способом:
StrToFloat(StringReplace(xls.Version, '.', DecimalSeparator, []))
Подписаться на:
Сообщения (Atom)