Tiskový výstup v Delphi má dvě možnosti, textovou a grafickou. Protože naši studenti vystačí s vytištěním jedné grafické stránky (graf, tabulka s výsledky), budeme se věnovat pouze druhé možnosti.
Jako úplně první musíme mezi použité unit přidat unitu printers, například na konec seznamu:
uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Printers;
Tato akce je stejná i pro textový režim. Přidáním této unity nám přibude objekt printer, se kterým můžeme dále pracovat. V dalším textu se nebudu držet didaktického postupu, jen naznačím posloupnost kroků, které musí program provést, aby mohl vytisknout stránku.
Někdy se začíná volbou tiskárny. Vlastnosti tiskárny
značně ovlivňují hodnoty, které musíme předávat
tiskárně. Tiskárnu nejsnáze zvolíme tak, že vyvoláme
dialog (komponentu z lišty "dialogs") PrintDialog
pro výběr tiskárny:
if printdialog1.execute then ;
PrintDialog1.Execute vrací true, pokud je stisknuto tlačítko OK, a false, pokud je stisknuto tlačítko Storno. Pokud se v druhém případě nemá nic vytisknout, je možné místo středníku napsat begin a end a mezi ně všechny ostatní příkazy pro tisk. Další možností je použít frázi
if not printdialog1.execute then exit;
Zbytek procedury v tomto případě neproběhne (pozor na otevřené soubory a podobně).
V liště dialogů je ještě komponenta PrinterSetupDialog.
Nebudeme ji asi potřebovat, protože vše podstatné je v
dialogovém okně předchozí komponenty pod tlačítkem
Vlastnosti (Properties). Výsledek nemusíme nikam přenášet. Unita
Printers smí být v celém projektu použita jen jednou,
takže k záměně nemůže dojít.
Zadávání dat pro grafický tisk zahájíme procedurou Printer.BeginDoc. Po sestavení grafické stánky ji do tiskové fronty Windows odešleme procedurou Printer.EndDoc. Pokud si to v průběhu práce rozmyslíme, můžeme zavolat proceduru Printer.Abort.
Poznámka - výše zmíněné dialogy lze samozřejmě volat jen před použitím Printer.BeginDoc. V průběhu samotného sestavování tiskové strany již nelze vlastnosti tiskárny měnit (až do Printer.EndDoc nebo Printer.Abort).
Pro tisk máme k dispozici plochu, která je dána rozlišením tiskárny. Je nám opět dostupná jako Canvas. Jeho parametry záleží na zvolené tiskárně a jejím nastavení (rozlišení, formát papíru a jiné). Následující program demonstruje rozlišení připojené tiskárny (volá nejprve dialog pro volbu tiskárny, protože jsem ověřoval rozlišení různých tiskáren). Program potřebuje zmíněnou komponentu z lišty Dialogs, tlačítko ke spuštění, a dva Labely:
if PrintDialog1.Execute then begin Printer.BeginDoc; Label1.Caption := IntToStr(Printer.PageWidth); Label2.Caption := IntToStr(Printer.PageHeight); Printer.Abort; end;
Nebudeme nic tisknout, proto Abort. Zkuste si zobrazit rozlišení pro několik různých tiskáren, popřípadě pro jiný formát papíru (A4 na výšku, A4 na šířku).
Při tisku vytváříme obraz na (pomyslné) ploše, které opět říkáme Canvas. Voláme tedy stejné procedury, jako pro obrázek. Můžeme používat i stejné barvy - v průběhu zadávání dat jsou barvy překládány do těch, které zná tiskárna.
Abychom stále nepsali Printer.Canvas. (u složitějších programů možná dokonce unit4.Form4.Printer.Canvas. a podobně), lze použít direktivu pro překladač, kterou mu řekneme, že v následujících řádcích bude většina prvků z tohoto objektu (platí i pro třídy a záznamy). V Pascalu se tak učiní direktivou with ve tvaru
with <jméno objektu> do <příkaz>;
Pro jeden konkrétní příkaz použití with nedává smysl, proto se takřka bezvýhradně používá ve spojení s programovými závorkami begin - end (vyjímkou mohou být složité podmíněné příkazy, cykly a podobně). Například obdélník o rozměru 100x100 pixelů je možné vykreslit buď příkazy
Printer.BeginDoc; Printer.Canvas.Pen.Width:=3; Printer.Canvas.Pen.Color:=clRed; Printer.Canvas.Rectangle(130,130,230,230); Printer.EndDoc;
nebo příkazy
with Printer do begin BeginDoc; Canvas.Pen.Width:=3; Canvas.Pen.Color:=clRed; Canvas.Rectangle(130,130,230,230); EndDoc; end;
nebo příkazy
Printer.BeginDoc; with Printer.Canvas do begin Pen.Width:=3; Pen.Color:=clRed; Rectangle(130,130,230,230); end; Printer.EndDoc;
Je na programátorovi, zda je pro něj vhodné tento postup použít. Je třeba si uvědomit, že každý objekt se pak bude hledat nejprve uvnitř deklarovaného objektu.
Pro tisk grafu vystačíme s příkazy MoveTo, LineTo, TextOut a nastavováním vlastností pera, jaké jsme si ukazovali minule. Nyní tedy již můžete napsat program, který nejprve namaluje graf zvolené funkce (sin(x), ...) na Canvas obrázku Image1, a pak tuto činnost zopakuje na Canvas tiskárny.
Poznámka: Zatímco Canvas obrázku a většiny jiných objektů lze číst bod po bodu, u tiskárny je propery "pixels" dostupná pouzde pro zápis, o čemž se můžeme přesvědčit, pokud hodnotu některého bodu změníme. Protože změna jednotlivého bodu by asi nemusela být vždy vidět, zkusme raději nadefinovat pomocnou proměnnou i a použít cyklus, například
for i:=1 to 1000 do pixels[150,i]:=clBlack;
Tiskárny v učebně jsou samozřejmě černobílé.
Poznámka. Také nám chybí návod, jak namalovat graf funkce. Co třeba takhle:
Mějme globální pole reálných čísel:
var a : array[0..100] of real;
nejprve je něčím naplníme, například načtením do memo1.lines[...], nebo v cyklu požadovanou funkcí:
for i:=0 to 100 do a[i]:=sin(i/100*pi*2);
pak určíme zvětšení, aby graf byl přes celý obrázek:
max:=-99999; min:=99999; for i:=0 to 100 do begin if max<a[i] then max:=a[i]; if min>a[i] then min:=a[i]; end; c:=(image1.height*0.9)/(max-min);
Z toho, že se zde vyskytl objekt image1, je patrné, že předpokládám, že se bude malovat do tohoto objektu. Je třeba jej vytvořit.
Vlastní vykreslení - nejprve se přenese kurzor na začátek, a pak se v cyklu vše pospojuje čarami:
image1.picture.canvas.pen.color:=clred; image1.picture.canvas.moveto(10, image1.height-10-round((a[0]-min)*c)); for i:=0 to 100 do image1.picture.canvas.lineto(10+i*3, image1.height-10-round((a[0]-min)*c));
Místo *3 by mělo být vodorovné zvětšení. Takhle to bude rozumně pracovat pro šířky obrázků od cca 315 do cca 350. Navíc by se ještě měly namalovat černě osy.
Neověřeno!