Delphi. Различные способы печати из приложений. Международный журнал экспериментального образования

Красота 22.09.2019
Красота

Delphi: заметки программиста. Часть 2 | Программирование | КомпьютерПресс 5"2001

Delphi: заметки программиста. Часть 2

примеры к данной статье скачать

Печать в Delphi

Объект Printer автоматически создается в случае, если в программе указана ссылка на модуль Printers. Этот объект предоставляет программисту все необходимое для того, чтобы научить приложение выводить данные на один из подключенных к компьютеру принтеров.

Вывод на принтер в Windows ничем не отличается от вывода на экран: в распоряжение программиста предоставляется свойство Canvas объекта Printer, содержащее набор чертежных инструментов, и методы, свойственные классу TCanvas. Размер листа бумаги в пикселах определяют свойства Height и Width, а набор принтерных шрифтов – свойство Fonts.

Печать текста

Существует множество способов печати текста на принтере. Прежде всего следует назвать глобальную процедуру AssignPrn (она определена в модуле Printers), позволяющую использовать принтер как текстовый файл и печатать текстовые строки с помощью процедуры WriteLn. В листинге 1 (PrintText.dpr) приведен полный текст модуля, на форме которого расположены многострочный текстовый редактор Memo1 и четыре кнопки: для выбора текстового файла и ввода его содержимого в редактор, для выбора нужного шрифта отображения/печати документа, для инициации процесса печати и для завершения работы программы.

Листинг 1

unit Unit1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Buttons; type TForm1 = class (TForm) Memo1: TMemo; Button1: TButton; Button2: TButton; OpenDialog1: TOpenDialog; BitBtn1: TBitBtn; Button3: TButton; FontDialog1: TFontDialog; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure Button3Click(Sender: TObject); private { Private declarations } public { Public declarations } end ; var Form1: TForm1; implementation uses Printers; // Эта ссылка обязательна! {$R *.DFM} procedure TForm1.Button1Click(Sender: TObject); // Выбор файла с текстом и его загрузка в редактор begin if OpenDialog1.Execute then Memo1.Lines.LoadFromFile(OpenDialog1.FileName) end ; procedure TForm1.Button3Click(Sender: TObject); // Выбор шрифта и связывание его с Memo1 begin if FontDialog1.Execute then Memo1.Font:= FontDialog1.Font end ; procedure // Печать содержимого редактора как вывод в текстовый файл var Prn: TextFile; k: Integer; begin AssignPrn(Prn); // Переназначаем вывод в файл на вывод в принтер Rewrite(Prn); // Готовим принтер к печати (аналог BeginDoc) { Для печати используем такой же шрифт, как и для показа в редакторе: } Printer.Canvas.Font:= Memo1.Font; // Цикл печати: for k:= 0 to Memo1.Lines.Count-1 do WriteLn(Prn, Memo1.Lines[k]); CloseFile(Prn); // Аналог EndDoc end ; end.

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

Значительно более гибкие средства обеспечивает свойство Printer.Canvas. Покажем, как с его помощью можно напечатать текст, содержащийся в редакторе Memo1 (PrintText.dpr , листинг 2):

Листинг 2

procedure TForm1.Button2Click(Sender: TObject); // Печать содержимого редактора c помощью свойства Printer.Canvas var Y,dY,X,k: Integer; S: String ; begin if Memo1.Lines.Count=0 then Exit; Screen.Cursor:= crHourGlass; with Printer do begin BeginDoc; with Canvas do begin Font:= Memo1.Font; dY:= TextHeight("1"); // Определяем высоту строки Y:= 3*dY; // Отступ от верхнего края листа X:= PageWidth div 15; // Отступ от левого края for k:= 0 to Memo1.Lines.Count-1 do begin // Выводим очередную строку TextOut(X,Y,Memo1.Lines[k]); // Смещаемся на следующую строку листа inc(Y,dY); if PageHeight-Y<2*dY then // Нижний край листа? begin // Да NewPage; // Переход на новый лист // Выводим номер страницы посередине листа: S:= "- "+IntToStr(PageNumber)+" -"; TextOut((PageWidth-TextWidth(S)) div 2, dy, S); // и отчеркиваем его от текста: MoveTo(X, 3*dy div 2); LineTo(PageWidth-X, 9*dy div 4); // Ордината первой строки: Y:= 3*dY end ; // if PageHeight-Y<2*dY end ; // for k:= 0 to Memo1.Lines.Count-1 do end ; // with Canvas do EndDoc; end ; // with Printer do Screen.Cursor:= crDefault; end;

Как можно увидеть, прямое обращение к чертежным инструментам свойства Canvas требует от программиста значительно бо льших усилий, но зато предоставляет ему полный контроль над печатным изображением.

Во многих случаях для печати документа и внесения в него элементарных средств форматирования (печать общего заголовка, заголовка на каждой странице, номеров страниц и т.п.) проще использовать специальные компоненты, расположенные на странице QReport палитры компонентов Delphi. Эти компоненты разработаны для создания отчетов по базам данных, но могут с успехом использоваться и для печати обычных документов (PrintText.dpr).

Наконец, очень хороших результатов можно достичь, используя специализированные средства просмотра/печати документов, как, например, текстовый процессор MS Word.

Печать изображений

Печать изображений может показаться очень сложным делом, однако свойство Printer.Canvas содержит метод:

procedure StretchDraw(const Rect: TRect; Graphic: TGraphic);

который легко справляется с этой задачей. При обращении к нему в качестве первого параметра указывается прямоугольная область, отводимая на поверхности листа для распечатки изображения, а в качестве второго - объект класса TGraphic, в котором хранится изображение, например:

with Printer do begin BeginDoc; Canvas.StretchDraw(Canvas.ClipRect, Image1.Picture.Graphic); EndDoc; end;

Отображение файла в память

Для работы с файлом динамической подкачки страниц виртуальной памяти в Windows 32 используется механизм отображения файлов в адресное пространство приложения. Соответствующие функции API доступны любому приложению и могут применяться к любому файлу (кстати, таким способом загружаются в адресное пространство процесса исполняемые файлы и DLL). В результате отображения приложение может работать с файловыми данными как с размещенными в динамической памяти. В большинстве случаев такая возможность не только повышает скорость работы с данными, но и предоставляет программисту уникальные средства обработки сразу всех записей файла. Например, он может с помощью единственного оператора проверить, входит ли заданный образец поиска в какую-либо строку текстового файла.

Отображение файла осуществляется в три приема. Вначале файл создается обращением к функции:

function FileCreate (FileName: String ): Integer;

или открывается с помощью:

function FileOpen (const FileName: String ; Mode: LongWord): Integer;

В обеих функциях FileName - имя файла, возможно, с маршрутом доступа. Параметр Mode определяет режим доступа к файлу и может принимать одно из следующих значений: fmOpenRead - только чтение; fmOpenWrite - только запись; fmOpenReadWrite - чтение и запись. С помощью операции or эти константы можно комбинировать с одной из следующих нескольких функций, регулирующих совместный доступ к файлу: fmShareExclusive - совместный доступ запрещен; fmShareDenyWrite - другим приложениям запрещается запись; fmShareDenyRead - другим приложениям запрещается чтение; fmSchareDenyNone - совместный доступ неограничен. Обе функции возвращают дескриптор созданного (открытого) файла или 0, если операция оказалась неудачной.

На втором этапе создается объект отображения в память. Для этого используется функция:

function CreateFileMapping (hFile: THandle; lpFileMappingAttributes: PSecurityAttributes; flProtect, dwMaximumSizeHigh, dwMaximumSizeLow: DWord; lpName: PChar): THandle;

Здесь hFile - дескриптор файла; lpFileMappingAttributes - указатель на структуру, в которой определяется, может ли создаваемый объект порождать дочерние объекты (обычно не может - NIL ); flProtect - определяет тип защиты, применяемый к окну отображения файла (см. об этом ниже); dwMaximumSizeHigh, dwMaximumSizeLow - соответственно старшие и младшие 32 разряда числа, содержащего размер файла (если вы будете отображать файлы длиной до 4 Гбайт, поместите в dwMaximumSizeHigh 0, если в dwMaximumSizeLow - длину файла; а если оба параметра равны 0, то размер окна отображения равен размеру файла); lpName - имя объекта отображения или NIL .

Параметр flProtect задает тип защиты, применяемый к окну просмотра файла, и может иметь одно из следующих значений: PAGE_READONLY - файл можно только читать (файл должен быть создан или открыт в режиме fmOpenRead); PAGE_READWRITE - файл можно читать и записывать в него новые данные (файл открывается в режиме fmOpenReadWrite); PAGE_WRITECOPY - файл открыт для записи и чтения, однако обновленные данные сохраняются в отдельной защищенной области памяти (отображенные файлы могут разделяться приложениями, в этом режиме каждое приложение сохраняет изменения в отдельной области памяти или участке файла подкачки); файл открывается в режиме fmOpenReadWrite или fmOpenWrite; (этот тип защиты нельзя использовать в Windows 95/98). С помощью операции or к параметру flProtect можно присоединить такие атрибуты: SEC_COMMIT - выделяет для отображения физическую память или участок файла подкачки; SEC_IMAGE - информация об атрибутах отображения берется из образа файла; SEC_NOCASHE - отображаемые данные не кэшируются и записываются непосредственно на диск; SEC_RESERVE - резервируются страницы раздела без выделения физической памяти.

Функция возвращает дескриптор объекта отображения или 0, если обращение было неудачным.

Наконец, на третьем этапе создается окно просмотра, то есть собственно отображение данных в адресное пространство программы.

function MapViewOfFile(hFileMappingObject: THandle;dwDesiresAccess: DWord; dwFileOffsetHigh, dwFileIffsetLow, dwNumberOfBytesToMap: DWord): Pointer;

Здесь hFileMappingObject - дескриптор объекта отображения; dwDesiresAccess - определяет способ доступа к данным и может иметь одно из следующих значений: FILE_MAP_WRITE - разрешает чтение и запись (при этом в функции CreateFileMapping должен использоваться атрибут PAGE_READWRITE); FILE_MAP_READ - разрешает только чтение (в функции CreateFileMapping должен использоваться атрибут PAGE_READONLY или PAGE_READWRITE); FILE_MAP_ALL_ACCESS - то же, что и FILE_MAP_WRITE; FILE_MAP_COPY - данные доступны для записи и чтения, однако обновленные данные сохраняются в отдельной защищенной области памяти (в функции CreateFileMapping должен использоваться атрибут PAGE_WRITECOPY); dwFileOffsetHigh, dwFileIffsetLow - определяют соответственно старшие и младшие разряды смещения от начала файла, начиная с которого осуществляется отображение; dwNumberOfBytesToMap - определяет длину окна отображения (0 - длина равна длине файла). Функция возвращает указатель на первый байт отображенных данных или NIL , если обращение к функции оказалось безуспешным.

После использования отображенных данных ресурсы окна отображения нужно освободить функцией:

function UnMapViewOfFile(lpBaseAddress: Pointer): BOOL;

единственный параметр обращения к которой должен содержать адрес первого отображенного байта, то есть адрес, возвращаемый функцией MapViewOfFile. Закрытие объекта отображения и самого файла осуществляется обращением к функции:

function CloseHandle(hObject: THandle).

В листинге 3 приводится текст модуля (File­­InMemory.dpr), который создает окно.

Листинг 3

unit Unit1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ComCtrls, Spin; type TForm1 = class (TForm) btMem: TButton; btFile: TButton; se: TSpinEdit; Label1: TLabel; pb: TProgressBar; Label2: TLabel; lbMem: TLabel; lbFile: TLabel; procedure btMemClick(Sender: TObject); procedure btFileClick(Sender: TObject); private { Private declarations } public { Public declarations } end ; var Form1: TForm1; implementation {$R *.DFM} procedure TForm1.btMemClick(Sender: TObject); // Создание файла методом его отображения type PReal = ^Real; var HFile, HMap: THandle; AdrBase, AdrReal: PReal; k: Integer; FSize: Cardinal; BegTime: TDateTime; begin BegTime:= Time; // Засекаем время пуска // Готовим ProgressBar: pb.Max:= se.Value; pb.Position:= 0; pb.Show; FSize:= se.Value * SizeOf(Real); // Длина файла HFile:= FileCreate("test.dat"); // Создаем файл if HFile = 0 then raise Exception.Create("Ошибка создания файла"); try // Отображаем файл в память HMap:= CreateFileMapping(HFile, NIL, PAGE_READWRITE, 0, FSize, NIL ); if HMap = 0 then // Ошибка: возбуждаем исключение raise Exception.Create("Ошибка отображения файла"); try // Создаем окно просмотра: AdrBase:= MapViewOfFile(HMap, FILE_MAP_WRITE, 0, 0, FSize); if AdrBase = NIL then // Ошибка: возбуждаем исключение raise Exception.Create("Невозможно просмотреть файл"); // Сохраняем начальный адрес для правильной ликвидации // окна просмотра: AdrReal:= AdrBase; for k:= 1 to se.Value do begin AdrReal^ := Random; // Помещаем в файл новое число // Перед наращиванием текущего адреса необходимо // привести его к типу Integer или Cardinal: AdrReal:= Pointer(Integer(AdrReal) + SizeOf(Real)); lbMem.Caption:= IntToStr(k); pb.Position:= k; Application.ProcessMessages; end ; // Освобождаем окно просмотра: UnmapViewOfFile(AdrBase) finally // Освобождаем отображение CloseHandle(HMap) end finally // Закрываем файл CloseHandle(HFile) end ; // Сообщаем время счета pb.Hide; lbMem.Caption:= TimeToStr(Time-BegTime) end ; procedure TForm1.btFileClick(Sender: TObject); // Создание файла обычным методом var F: File of Real; k: Integer; BegTime: TDateTime; R: Real; // Буферная переменная для обращения к Write begin BegTime:= Time; // Засекаем начальное время счета // Готовим ProgressBar: pb.Max:= se.Value; pb.Position:= 0; pb.Show; // Создаем файл: AssignFile(F, "test.dat"); Rewrite(F); for k:= 1 to se.Value do begin R:= Random; // Параметрами обращения к Write Write(F, R); // могут быть только переменные lbFile.Caption:= IntToStr(k); pb.Position:= k; Application.ProcessMessages; end ; CloseFile(F); pb.Hide; lbFile.Caption:= TimeToStr(Time-BegTime) end ; end.

Проект создает дисковый файл, состоящий из 100 тыс. случайных вещественных чисел (можно выбрать другую длину файла, если изменить значение редактора Длина массива). Файл с именем test.dat создается путем отображения файла в память (кнопка Память ) и традиционным способом (кнопка Файл ). В обоих случаях показывается время счета. Чем больше частота процессора и объем свободной оперативной памяти, тем больше будет разница во времени (листинг 3).

О таймере

Компонент Timer (таймер) служит для отсчета интервалов реального времени. Его свойство Interval определяет интервал временив миллисекундах, который должен пройти от включения таймера до наступления события OnTimer. Таймер включается при установке значения True в его свойство Enabled. Единожды включенный таймер все время будет возбуждать события OnTimer до тех пор, пока его свойство Enabled не примет значения False.

Следует учесть, что в силу специфики реализации стандартного аппаратного таймера IBM-совместимого компьютера минимальный реально достижимый интервал отсчета времени не может быть меньше 55 мс (этот интервал называется тиком ), более того, любой интервал времени, отсчитываемый с помощью таймера, всегда кратен 55 мс. Чтобы убедиться в этом, проведите эксперимент, в котором подсчитывается среднее время между двумя срабатываниями таймера (Timer.dpr):

  1. Начните новый проект с пустой формой и положите на нее компонент TTimer.
  2. Установите в свойство Enabled таймера значение False.
  3. Напишите такой модуль главной формы (листинг 4):

Листинг 4

unit Unit1; interface uses type TfmExample = class (TForm) Panel1: TPanel; bbRun: TBitBtn; bbClose: TBitBtn; edInput: TEdit; lbOutput: TLabel; mmOutput: TMemo; Timer1: TTimer; procedure procedure Timer1Timer(Sender: TObject); procedure private { Private declarations } BegTime: TDateTime; // Начальное время цикла Counter: Integer; // Счетчик цикла public { Public declarations } end ; var fmExample: TfmExample; implementation {$R *.DFM} procedure // Запускает таймер. edInput содержит период его срабатывания. var Delay: Word; begin // Проверяем задание интервала if edInput.Text="" then Exit; try Delay:= StrToInt(edInput.Text); except ShowMessage("Ошибка в записи числа"); edInput.SelectAll; edInput.SetFocus; Exit end ; Counter:= 0; // Сбрасываем счетчик Timer1.Interval:= Delay; // Устанавливаем интервал BegTime:= Time; // Засекаем время Timer1.Enabled:= True; // Пускаем таймер Screen.Cursor:= crHourGlass end ; procedure TfmExample.Timer1Timer(Sender: TObject); var h, m, s, ms: Word; const MaxCount = 55; // Количество срабатываний таймера begin Counter:= Counter + 1; // Наращиваем счетчик срабатываний if Counter=MaxCount then // Конец цикла? begin // - Да Timer1.Enabled:= False; // Останавливаем таймер // Находим среднее время срабатывания: DecodeTime((Time-BegTime)/MaxCount, h, m, s, ms); mmOutput.Lines.Add(// Выводим результат Format("Задано %s ms. Получено %d ms.", )); edInput.Text:= ""; // Готовим следующий запуск edInput.SetFocus; Screen.Cursor:= crDefault end ; end ; procedure begin edInput.SetFocus end ; end.

Необходимость нескольких (MaxCount) срабатываний для точного усреднения результата связана с тем, что системные часы обновляются каждые 55 мс. После запуска программы и ввода 1 как требуемого периода срабатывания в редакторе mmOutput вы увидите строку

Задано 1 ms. Получено 55 ms.

в которой указывается, какое реальное время разделяет два соседних события OnTimer. Если вы установите период таймера в диапазоне от 56 до 110 мс, в строке будет указано 110 ms и т.д. (в силу дискретности обновления системных часов результаты могут несколько отличаться в ту или иную сторону).

В ряде практически важных областей применения (при разработке игр, в системах реального времени для управления внешними устройствам и т.п.) интервал 55 мс может оказаться слишком велик. Современный ПК имеет мультимедийный таймер, период срабатывания которого может быть от 1 мс и выше, однако этот таймер не имеет компонентного воплощения, поэтому для доступа к нему приходится использовать функции API.

Общая схема его использования такова. Сначала готовится процедура обратного вызова (call back) с заголовком:

procedure TimeProc(uID, uMsg: UINT; dwUser, dw1, dw2: DWORD); stdcall ;

Здесь uID - идентификатор события таймера (см. об этом ниже); uMsg - не используется; dwUser - произвольное число, передаваемое процедуре в момент срабатывания таймера; dw1, dw2 - не используются.

Запуск таймера реализуется функцией:

function timeSetEvent(uDelay, uResolution: UINT; lpTimeProc: Pointer; dwUser: DWORD; fuEvent: UINT): UINT; stdcall ; external "winmm.dll";

Здесь uDelay - необходимый период срабатывания таймера (в мс); uResolution - разрешение таймера (значение 0 означает, что события срабатывания таймера будут возникать с максимально возможной частотой; в целях снижения нагрузки на систему вы можете увеличить это значение); lpTimeProc - адрес процедуры обратного вызова; dwUser - произвольное число, которое передается процедуре обратного вызова и которым программист может распоряжаться по своему усмотрению; fuEvent - параметр, управляющий периодичностью возникновения события таймера: TIME_ONESHOT (0) - событие возникает только один раз через uDelay миллисекунд; TIME_PERIODIC (1) - события возникают периодически каждые uDelay мс. При успешном обращении функция возвращает идентификатор события таймера и 0, если обращение было ошибочным.

Таймер останавливается, и связанные с ним системные ресурсы освобождаются функцией:

function timeKillEvent(uID: UINT): UINT; stdcall ; external "winmm.dll";

Здесь uID - идентификатор события таймера, полученный с помощью timeSetEvent.

В следующем примере (Timer.dpr) иллюстрируется использование мультимедийного таймера (листинг 5).

Листинг 5

unit Unit1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Buttons, ExtCtrls; type TfmExample = class (TForm) Panel1: TPanel; bbRun: TBitBtn; bbClose: TBitBtn; edInput: TEdit; lbOutput: TLabel; mmOutput: TMemo; procedure bbRunClick(Sender: TObject); procedure FormActivate(Sender: TObject); private { Private declarations } public { Public declarations } end ; var fmExample: TfmExample; implementation {$R *.DFM} // Объявление экспортируемых функций: function timeSetEvent(uDelay, uReolution: UINT; lpTimeProc: Pointer; dwUser: DWORD; fuEvent: UINT): Integer; stdcall ; external "winmm"; function timeKillEvent(uID: UINT): Integer; stdcall ; external "winmm"; // Объявление глобальных переменных var uEventID: UINT; // Идентификатор события таймера BegTime: TDateTime; // Засекаем время < Counter: Integer; // Счетчик повторений Delay: Word; // Период срабатывания procedure ProcTime(uID, msg: UINT; dwUse, dw1, dw2: DWORD); stdcall ; // Реакция на срабатывание таймера (процедура обратного вызова) var h, m, s, ms: Word; // Переменные для декодирования времени const MaxCount = 55; // Количество повторений begin timeKillEvent(uEventID); // Останавливаем таймер Counter:= Counter+1; // Наращиваем счетчик if Counter=MaxCount then // Конец цикла? begin // - Да: декодируем время DecodeTime((Time-BegTime)/MaxCount, h, m, s, ms); fmExample.mmOutput.Lines.Add(// Сообщаем результат Format("Задано %s ms. Получено %d ms", )); fmExample.edInput.Text:= ""; // Готовим повторение fmExample.edInput.SetFocus end else // - Нет: вновь пускаем таймер end ; procedure TfmExample.bbRunClick(Sender: TObject); // Запускает таймер. edInput содержит требуемый период. begin // Проверяем задание периода if edInput.Text="" then Exit; try Delay:= StrToInt(edInput.Text) except ShowMessage("Ошибка ввода числа"); edInput.SelectAll; edInput.SetFocus; Exit end ; Counter:= 0; // Сбрасываем счетчик BegTime:= Time; // Засекаем время // Запускаем таймер: uEventID:= timeSetEvent(Delay,0,@ProcTime,0,1); if uEventID=0 then ShowMessage("Ошибка запуска таймера") end ; procedure TfmExample.FormActivate(Sender: TObject); begin edInput.SetFocus end ; end. 1

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

печать базы данных

компонент QuickReport

установка пакета

полосы печати

колонтитул

графический контекст принтера

диалоги печати

задание на печать

1. Архангельский А.Я. Программирование в Delphi 7. – М.: OOO «Бином-Пресс», 2003 г. – 1152с: ил.

2. Фленов М. Библия Delphi. – БХВ-Петербург, 2008. – 880 с.

Печать отчетов

Любое приложение по работе с базами данных можно считать неполным, если в приложении не реализована печать данных. Если во многих других приложениях без печати можно обойтись, а в некоторых из них это все лишь красивый маркетинг, то приложениями для работы с базами данных, у которых отсутствует печать, редко кто будет пользоваться.

В Delphi7 для предварительного просмотра и печати отчетов существует достаточный набор компонентов под названием QuickReport.

Надо заметить, что раздел справки по этой теме, находящийся в файле «c:\Program Files\Borland\Delphi7\Help\QUICKRPT.HLP», достаточно скудный. Исходных модулей по данным компонентам тоже нет. Все компоненты расположены в откомпилированных файлах ‘dcu’. Поэтому необходимо привести начальные шаги по использованию имеющихся возможностей печати в Delphi7. Здесь, и в дальнейшем упоминается путь по умолчанию «c:\Program Files\Borland\Delphi7».

Начать следует с того, чтобы проверить факт установки компонентов этой группы. Для этого можно воспользоваться подпунктом главного меню View-Component List, и попробовать там найти, например, компонент TQuickRep.

В большинстве случаев, при установке по умолчанию, нужный пакет не установлен. Об этом также упомянуто в файле: «c:\Program Files\Borland\Delphi7\Demos\Quickrpt\QReport_README.txt», в этом файле также описан и процесс установки.

Для того, чтобы установить пакет нужно выполнить следующие действия.

В главном меню Delphi7 выбираем пункт «Component», а в нем подпункт «Install Packages». В открывшемся диалоговом окне выбираем кнопку “Add”, находим в “bin”-директории Delphi (...\Borland\Delphi7\bin) файл “dclqrt70.bpl”.

После установки, в конце палитры компонентов появится вкладка QReport; а также в репозитории, на вкладке New - элемент Report.

Вкладку можно переместить в удобное для работы место, как и сами компоненты на вкладке. Для этого в главном меню Delphi7 выбираем пункт «Component», а в нем подпункт «Configure Palette», и выполняем нужные действия.

Формирование отчета можно начинать двумя путями. Либо разместить на форме компонент QuickRep с вкладки QReport, либо использовать элемент Report из Репозитория. Для работы практичнее использовать второй вариант.

Оба элемента построены на базе одного класса - TquickRep. Однако во время выполнения, содержимое компонента QuickRep на форме будет выглядеть почти также как в конструкторе. А для отображения самого отчета нужно выполнить еще некоторые действия. Так, при вызове кода QuickRep1.Preview (например, в обработчике события кнопки), предварительный просмотр отчета все равно будет показан совершенно в отдельном окне. Поэтому во время выполнения, демонстрация формы с расположенным на ней компонентом QuickRep не несет какой-либо конструктивности. При втором варианте для данного компонента во время дизайна создается своя форма, где можно настроить внешний вид отчета. А его использование, как уже упоминалось, выполняется другими методами.

Во время работы программы, непосредственно в окне отчета можно просматривать собранную информацию из базы данных - перелистывая страницы, меняя масштаб просмотра. И непосредственно из этого окна можно выполнять печать. Также печать можно выполнить вызовом кода: QuickRep1.Print

При двойном щелчке по компоненту QuickRep в режиме конструктора можно настроить свойства будущего документа: размер бумаги, поля и т.д. Что равносильно выбору пункта Report Setting из контекстного меню компонента.

ПРИМЕР

Будем использовать элемент Report из Репозитория. По умолчанию имеет имя QuickReport2.

Помещаем на него (в любое место) компонент Table с вкладки BDE.

Для дальнейшей работы воспользуемся демонстрационными примерами баз данных, устанавливаемых вместе с Delphi.

Настраиваем свойства компонента Table1: DatebaseName выставляем в ‘DBDEMOS’, TableName в ‘animals.dbf’, Active в ‘true’.

На вкладке QReport берем компонент QRBand и помещаем на QuickReport2.

Меняем свойство BandType у компонента QRBand на rbDetail.

Помещаем на этот полосу компонент QRDBText.

Настраиваем у него свойство DataSet в ‘Table1’, DataField в ’NAME’.

Помещаем на полосу компонент QRDBImage.

Настраиваем у него свойство DataSet в ‘Table1’ DataField в ‘BMP’.

Из контекстного меню QuickReport2 выбираем пункт Preview (в режиме конструктора). Аналогично (в режиме выполнения) можно выполнить код

QuickRep1.Preview. Не забываем подключить Unit2.

В результате отобразится окно предварительного просмотра данных. Но в нем отображается только первая запись из набора данных.

Для того, чтобы увидеть весь набор нужно у самого компонента QuickReport2 настроить свойство DataSet в ‘Table1’.

Вот так, достаточно просто можно работать с компонентами QuickReport.

Несколько советов по дальнейшей работе.

В отношении свойства BandType компонента QRBand.

По умолчанию имеет тип rbTitle (полоса заголовка). Появляется только на первой странице.

RbColumnHeader (полоса заголовков столбцов) отображается на каждой странице.

Для отображения информации на указанных полосах обычно используют компонент QRLabel (компонент Label с вкладки Standart - не отображается).

RbDetail (полоса деталей). Как было показано в примере - выводит весь набор из базы данных.

Вообще, реализуется достаточно большой набор полос. Есть колонтитулы, итоговые полосы и т.д. Полоса с указанным типом, после помещения ее на компонент QuickReport, сразу занимает свое место.

Функциональность компонентов с вкладки QReport становится очевидной на практике. Аналогично можно сказать про полосы и их выравнивание в режиме конструктора.

Будет не лишним обратить внимание на пару примеров из папки:

“C:\Program Files\Borland\Delphi7\Demos\Quickrpt\”,которые устанавливаются вместе с Delphi.

Из событий QuickReport стоит отметить OnPreview и OnNeedData.

1-е можно использовать для вывода своего собственного окна просмотра вместо заданного по умолчанию.

2-е используется, когда источник данных не является базой данных VCL. Например, вы можете создать отчет для списка строк, массива или текстового файла .

Печать текста и графики

В случаях, когда необходимо выполнить вывод на печать текста, либо графики не из базы данных, следует использовать возможности, построенные на базе класса TPrinter.

Чтобы объект TPrinter стал доступен в проекте, необходимо добавить в раздел uses модуль Printers. Объект TPrinter не надо инициализировать. Достаточно только подключить модуль и объект становится доступным через переменную Printer, которая объявлена в данном модуле. Работа с объектом TPrinter базируется на его свойстве TCanvas. Поскольку данное свойство представляет собой графический контекст, то нужно быть готовым к тому, что вывод текста выполняется также в виде графики. В тоже время, свойство TCanvas инкапсулирует в себе низкоуровневые функции по работе с графическим контекстом, что значительно облегчает работу программиста.

Перечислим основные Свойства объекта TPrinter:

Aborted - переменная типа Boolean. Если она равна true, то пользователь прекратил вывод информации на принтер.

Canvas - объект типа TCanvas. Это холст, на который можно выводить информацию в графическом виде.

Copies - количество копий документа необходимых для печати.

Fonts - список шрифтов поддерживаемых принтером.

Handle - здесь храниться дескриптор контекста принтера. Через него можно воспользоваться напрямую функциями WinAPI.

Orientation - ориентация страницы. Это свойство может иметь одно из следующих значений: poPortrait - книжный, или poLandscape - альбомный.

PageHeight - высота страницы в пикселях.

PageWidth - ширина страницы в пикселях.

PageNumber - номер текущей печатаемой страницы.

PrinterIndex - число, которое указывает на номер активного принтера.

Printers - список типа TStrings установленных в системе принтеров.

Printing - если это свойство равно true, то принтер в данный момент печатает.

Title - заголовок или просто текстовое описание печатаемого документа. Этот заголовок будет отображаться во время печати в менеджере печати.

Методы объекта TPrinter:

Abort - прерывает текущую печать.

BeginDoc - начало печатаемого документа.

EndDoc - конец документа.

GetPrinter - получить индекс текущего принтера.

NewPage - новая страница документа.

Refresh - обновить информацию о шрифтах

В начале кода, выводящего на печать, будет разумно, а также профессионально, воспользоваться компонентом TPrintDialog. Для того, чтобы пользователь мог задать особенности печати. Это, как минимум, диапазон печатаемых страниц, количество копий и т.п.

Настройка таких свойств печати, как ориентация бумаги или выбор принтера автоматически помещается компонентом TPrintDialog в объект TPrinter.

Возможно, программист захочет отойти от предлагаемых стандартных диалогов TPrintDialog, TPrinterSetupDialog, и реализовать свои, в каком-либо собственном стиле. В этом случае потребуется напрямую обращаться к свойствам Orientation, Printers, PrinterIndex и методам GetPrinter, SetPrinter объекта TPrinter. Если эта реализация окупается здравым смыслом - почему бы нет. Но при этом нужно быть готовым к дополнительным затратам разработки. И, конечно же, разработанный пользовательский стиль диалога печати должен отвечать некоторым стандартам, и быть понятным и удобным для пользователя.

Следующим шагом будет настройка нужного шрифта, например так: Printer.Canvas.Font:= Memo1.Font;

И необходимо внести коррективу размера шрифта под разрешение контекста принтера, например так:

LineHeight:= Abs(

MulDiv(Printer.Canvas.Font.Size,

GetDeviceCaps(Printer.Handle, LOGPIXELSY), 72));

Рассмотрим эту формулу более подробно.

Переменная LineHeight будет хранить высоту строки уже на канве принтера. Функция WinAPI GetDeviceCaps ищет информацию об указанном устройстве. В нашем случае особый интерес представляет параметр LOGPIXELSY. Он возвращает число пикселей в логическом дюйме по высоте контекста. Свойство Font.Size по умолчанию ориентировано на разрешение равное 72. Поэтому необходимо выполнить формулу Font.Size * LOGPIXELSY / 72. На большинстве принтеров LOGPIXELSY равно 600. WinAPI функция MulDiv умножает два 32-разрядных значения и затем делит 64-битовый результат на третье 32-разрядное значение. Возвращаемое значение округлено вверх или вниз к самому близкому целому числу. Использование этой функции удобнее обычной арифметики тем, что функция кроме самого вычисления совмещает в себе округление.

К полученному результату добавляем 40 % на межстрочный интервал

Inc(LineHeight, (LineHeight*4) div 10);

Определение количества строк на странице.

LinesPerPage:= (Printer.PageHeight div LineHeight) ;

После этого можно приступать непосредственно к формированию задания на печать. Для этого вызывают метод

Printer.BeginDoc;

и в цикле начинают выполнять вывод строк. При этом нужно помнить, что при работе с контекстом принтера следует придерживаться графических функций.

Для вывода текста обычно используют Printer.Canvas.TextOut. Для вывода заголовков, либо колонтитулов, удобно воспользоваться WinAPI функцией DrawText, которая удобным образом центрирует текст на странице.

При выводе строк в цикле имеет смысл организовать переменную-счетчик, которая будет следить за достижением выведенного количества строк значения переменной LinesPerPage. И в этом случае, новая страница начинается при помощи метода Printer.NewPage.

Когда задание на печать сформировано, должен быть вызван метод Printer.EndDoc. И только после этого начнется непосредственная печать документа на принтере.

Замечание для тех случаев, когда понадобится воспользоваться функциями WinAPI напрямую, например для рисования, типа:

Printer.BeginDoc;

Rectangle(Printer.Canvas.Handle, 0,0,500,500);

то контекст канвы принтера одинаков и через

Printer.Canvas.Handle

Замечена разница:

Обращение к дескриптору 1-го типа за пределами BeginDoc, EndDoc, например:

memo1.Lines.Append(inttostr(Printer.Canvas.Handle));

поднимает исключение “raised exception class EPrinter with message ‘Printer is not currently printing”.

Вариант c дескриптором 2-го типа

memo1.Lines.Append(inttostr(Printer.Handle));

работает без исключения, и номер дескриптора возвращает правильный - такой же, как и внутри BeginDoc, EndDoc в процессе печати.

Однако вызов

Rectangle(Printer.Handle,500,50,700,800);

без входа в BeginDoc, EndDoc - печати конечно не делает. Происходит “мягкое” игнорирование без исключения.

Также, для печати текста без какого-либо форматирования, можно использовать код:

Writeln(f, ‘Hello world’);

В этом случае, также необходимо подключить модуль Printers. Поскольку в данном коде объект TPrinter используется неявным образом.

В статье рассмотрены основные приемы программирования печати. Выбор нужного способа за программистом, и зависит от конкретной ситуации.

Библиографическая ссылка

Бартосик Ф.М., Молдаванова И.Г., Мурых Е.Л., Авилова Е.К. ПЕЧАТЬ В DELPHI 7 // Международный журнал экспериментального образования. – 2015. – № 12-3. – С. 363-366;
URL: http://expeducation.ru/ru/article/view?id=9127 (дата обращения: 06.04.2019). Предлагаем вашему вниманию журналы, издающиеся в издательстве «Академия Естествознания»

Delphi имеет стандартный объект для доступа к принтеру - TPRINTER, находящийся в модуле PRINTERS. В этом модуле имеется переменная Printer:Tpinter, что избавляет от необходимости описывать свою. Он позволяет выводить данные на печать и управлять процессом печати. Правда, в некоторых версиях Delphi 1 он имеет "глюк" - не работают функции Draw и StrethDraw. Но эта проблема поправима - можно использовать функции API. Далее приведены основные поля и методы объекта Printers:

СВОЙСТВА

  • Aborted: boolean - Показывает, что процесс печати прерван
  • Canvas: Tcanvas - Стандартный Canvas, как у любого графического объекта. Он позволяет рисовать на листе бумаге графику, выводить текст... . Тут есть несколько особенностей, они описаны после описания объекта.
  • Fonts: Tstrings - Возвращает список шрифтов, поддерживаемых принтером
  • Handle: HDS - Получить Handle на принтер для использования функций API (см. Далее)
  • Orientation: TprinterOrientation - Ориентация листа при печати: (poPortrait, poLandscape)
  • PageHeight: integer - Высота листа в пикселах
  • PageNumber: integer - Номер страницы, увеличивается на 1 при каждом NewPage
  • PageWidth: integer - Ширина листа в пикселах
  • PrinterIndex: integer - Номер используемого принтера по списку доступных принтеров Printers
  • Printers: TStrings - Список доступных принтеров
  • Printing: boolean - Флаг, показывающий, что сейчас идет процесс печати
  • Title: String - Имя документа или приложения. Под этим именем задание на печать регистрируется в диспетчере печати

МЕТОДЫ

  • AssignPrn(f: TextFile) - Связать текстовый файл с принтером. Далее вывод информации в этот файл приводит к ее печати. Удобно в простейших случаях.
  • Abort - Сбросить печать
  • BeginDoc - Начать печать
  • NewPage - Начать новую страницу
  • EndDoc - Завершить печать.

Особенности работы с TPrinter

  1. После команды BeginDoc шрифт у Canvas принтера сбрасывается и его необходимо задавать заново
  2. Все координаты даны в пикселах, а для нормальной работы необходимы миллиметры (по двум очевидным причинам: очень трудно произвести разметку страницы в пикселах (особенно если необходима точность), и, главное, при изменении разрешающей способности принтера будет изменяться число точек на дюйм, и все координаты "поедут".
  3. У TPrinter информация о принтере, по видимому, определяются один раз - в момент запуска программы (или смены принтера). Поэтому изменение настроек принтера в процессе работы программы может привести к некорректной работе, например, неправильной печать шрифтов True Type.

Определение параметров принтера через API

Для определения информации о принтере (плоттере, экране) необходимо знать Handle этого принтера, а его можно узнать объекта TPrinter - Printer.Handle.


GetDevice(Handle:HDC; Index:integer):integer;

Index - код параметра, который необходимо вернуть.

Для Index существует ряд констант:

  • DriverVersion - вернуть версию драйвера
  • Texnology - Технология вывода, их много, основные
  • dt_Plotter - плоттер
  • dt_RasPrinter - растровый принтер
  • dt_Display - дисплей
  • HorzSize - Горизонтальный размер листа (в мм)
  • VertSize - Вертикальный размер листа (в мм)
  • HorzRes - Горизонтальный размер листа (в пикселах)
  • VertRes - Вертикальный размер листа (в пикселах)
  • LogPixelX - Разрешение по оси Х в dpi (пиксел /дюйм)
  • LogPixelY - Разрешение по оси Y в dpi (пиксел /дюйм)

Кроме перечисленных еще около сотни, они позволяют узнать о принтере практически все. Параметры, возвращаемые по LogPixelX и LogPixelY очень важны - они позволяют произвести пересчет координат из миллиметров в пиксели для текущего разрешения принтера.

Пример таких функций:


Данную методику можно с успехом применять для печати картинок - зная размер картинки можно пересчитать ее размеры в пикселах для текущего разрешения принтера, масштабировать, и затем уже распечатать. Иначе на матричном принтере (180 dpi) картинка будет огромной, а на качественном струйнике (720 dpi) - микроскопической.

Я производил печать следующим образом:


procedure TForm6.SpeedButton1Click(Sender: TObject); var PRect: Trect; PBitMap: TBitmap; begin PBitmap:=TBitMap.Create; PBitmap.LoadFromFile("C:\1.bmp"); with PRect do begin left:=0; top:=0; right:=Printer.PageWidth; Bottom:=Printer.PageHeight; end ; with printer do begin BeginDoc; font.name :="Times New Roman"; Canvas.StretchDraw(PRect,Bitmap); EndDoc; end ; PBitmap.Free; end ;

Обзор

Печать в текстовом режиме

Вывод содержимого формы на печать

Графическая печать (объект TPrinter)

Пример

  1. Обзор
  2. В данной статье рассказывается о возможных способах вывода информации на печать из программы, созданной в Delphi. Рассматривается вывод документа в текстовом режиме принтера, вывод графики с помощью объекта TPrinter и печать содержимого формы. О выводе на печать отчетов с помощью генератора отчетов ReportSmith рассказывается ниже.
  3. Печать в текстовом режиме

Если Вам нужно напечатать на принтере документ в текстовом режиме, то это делается следующим образом. С принтером Вы работаете, как с обычным текстовым файлом, за исключением того, что вместо процедуры AssignFile нужно вызывать процедуру AssignPrn. В примере на принтер выводится одна строка текста:

procedure TForm1.Button1Click(Sender: TObject);

To_Prn: TextFile;

begin

AssignPrn(To_Prn);

Rewrite(To_Prn);

Writeln(To_Prn, "Printer in Text Mode");

CloseFile(To_Prn);

end;

Здесь необходимо, видимо, пояснить, что по сравнению с BP 7.0 в Delphi изменены названия некоторых функций и переменных в модуле System:

  • AssignFile вместо Assign
  • CloseFile вместо Close
  • TextFile вместо Text
    1. Вывод содержимого формы на печать
    2. Иногда в программе требуется просто получить твердую копию экранной формы. В Delphi это делается более, чем просто - у объекта TForm есть метод Print, который и нужно вызвать в нужный момент.
    3. Графическая печать (объект TPrinter)

И все же, более интересно, как из программы созданной в Delphi можно вывести на печать графическую информацию. Для этого есть специальный объект Printer (класса TPrinter). Он становится доступен, если к программе подключить модуль Printers (т.е. добавить имя модуля в разделе uses ). С помощью этого объекта печать на принтере графической информации становится не сложнее вывода этой информации на экран. Основным является то, что Printer предоставляет разработчику свойство Canvas (работа с канвой описана в предыдущем уроке) и методы, выводящие содержание канвы на принтер. Рассмотрим подробнее свойства и методы объекта Printer.

Свойства Printer:

  • Aborted - тип булевский; показывает, прервал ли пользователь работу принтера методом Abort.

    Canvas - канва, место для вывода графики; работа с Canvas описана в Уроке 5.

    Fonts - список доступных шрифтов.

    Handle - используется при прямых вызовах Windows API.

    Orientation - ориентация страницы, вертикально или горизонтально.

    PageWidth , PageHeight , PageNumber - соответственно ширина, высота и номер страницы.

    Printers перечисляет все установленные в системе принтеры, а

    PrinterIndex указывает, какой из них является текущим. Чтобы печатать на принтере по умолчанию здесь должно быть значение -1.

    Printing - тип булевский; показывает, начата ли печать (методом BeginDoc ).

    Title - заголовок для Print Manager и для заголовка перед выводом на сетевом принтере.

  • Методы Printer:

    Abort - прерывает печать, начатую методом BeginDoc

  • BeginDoc - вызывается перед тем, как начать рисовать на канве.

    EndDoc - вызывается когда все необходимое уже нарисовано на канве, принтер начинает печатать именно после этого метода.

    NewPage - переход на новую страницу.

  • Остальными методами объекта в обычных случаях пользоваться не нужно.

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

    • выполняется метод BeginDoc
    • на канве (Canvas) рисуем все, что нужно
    • при необходимости разместить информацию на нескольких листах вызываем метод NewPage
    • посылаем нарисованное на принтер, выполняя метод EndDoc
      1. Пример

    В примере (проект PRINTS.DPR, рис.1) реализованы все три вышеописанных ситуации.


      Содержание урока 6:
      Обзор
      Печать в текстовом режиме
      Вывод содержимого формы на печать
      Графическая печать (объект TPrinter)
      Пример ex06.zip
        1. Обзор

        2. В данной статье рассказывается о возможных способах вывода информации на печать из программы, созданной в Delphi. Рассматривается вывод документа в текстовом режиме принтера, вывод графики с помощью объекта TPrinter и печать содержимого формы. О выводе на печать отчетов с помощью генератора отчетов ReportSmith рассказывается ниже.

        3. Печать в текстовом режиме
    Если Вам нужно напечатать на принтере документ в текстовом режиме, то это делается следующим образом. С принтером Вы работаете, как с обычным текстовым файлом, за исключением того, что вместо процедуры AssignFile нужно вызывать процедуру AssignPrn. В примере на принтер выводится одна строка текста :

    procedure TForm1.Button1Click(Sender: TObject);
    var
    To_Prn: TextFile;
    begin
    AssignPrn(To_Prn);
    Rewrite(To_Prn);
    Writeln(To_Prn, "Printer in Text Mode");
    CloseFile(To_Prn);
    end;

    Здесь необходимо, видимо, пояснить, что по сравнению с BP 7.0 в Delphi изменены названия некоторых функций и переменных в модуле System:

      • AssignFile вместо Assign
      • CloseFile вместо Close
      • TextFile вместо Text
        1. Вывод содержимого формы на печать

        2. Иногда в программе требуется просто получить твердую копию экранной формы. В Delphi это делается более, чем просто - у объекта TForm есть метод Print, который и нужно вызвать в нужный момент.

        3. Графическая печать (объект TPrinter )
    И все же, более интересно, как из программы созданной в Delphi можно вывести на печать графическую информацию. Для этого есть специальный объект Printer (класса TPrinter). Он становится доступен, если к программе подключить модуль Printers (т.е. добавить имя модуля в разделе uses ) . С помощью этого объекта печать на принтере графической информации становится не сложнее вывода этой информации на экран. Основным является то, что Printer предоставляет разработчику свойство Canvas ( работа с канвой описана в предыдущем уроке) и методы, выводящие содержание канвы на принтер. Рассмотрим подробнее свойства и методы объекта Printer.

    Свойства Printer: Aborted - тип булевский ; показывает, прервал ли пользователь работу принтера методом Abort.

    Canvas - канва, место для вывода графики ; работа с Canvas описана в Уроке 5.

    Fonts - список доступных шрифтов.

    Handle - используется при прямых вызовах Windows API.

    Orientation - ориентация страницы, вертикально или горизонтально.

    PageWidth , PageHeight , PageNumber - соответственно ширина, высота и номер страницы.

    Printers перечисляет все установленные в системе принтеры, а

    PrinterIndex указывает, какой из них является текущим. Чтобы печатать на принтере по умолчанию здесь должно быть значение -1.

    Printing - тип булевский ; показывает, начата ли печать (методом BeginDoc ).

    Title - заголовок для Print Manager и для заголовка перед выводом на сетевом принтере.

    Методы Printer:

    Abort - прерывает печать, начатую методом BeginDoc BeginDoc - вызывается перед тем, как начать рисовать на канве.

    EndDoc - вызывается когда все необходимое уже нарисовано на канве, принтер начинает печатать именно после этого метода.

    NewPage - переход на новую страницу. Остальными методами объекта в обычных случаях пользоваться не нужно.

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

    • выполняется метод BeginDoc
    • на канве (Canvas) рисуем все, что нужно
    • при необходимости разместить информацию на нескольких листах вызываем метод NewPage
    • посылаем нарисованное на принтер, выполняя метод EndDoc
        1. Пример ex06.zip
    В примере (проект PRINTS.DPR, рис.1 ) реализованы все три вышеописанных ситуации.



    Рекомендуем почитать

    Наверх