Živě.cz o počítačích a internetu

Umíme to s Delphi: 109. díl – vytváříme a používáme zdroje (resources) krok za krokem

Václav Kadlec - 17.11. 2003

Minulý díl našeho seriálu odbočil od problematiky resources, kterou jsme se zaobírali v předešlých dvou částech, a zaměřil se na několik zajímavých matematických, finančních a statistických funkcí. Dnes se ovšem – jak jsem slíbil – ke zdrojům opět vrátíme a předvedeme si práci s nimi prakticky. A to samozřejmě hlavně v souvislosti s vývojovým prostředím Delphi.

Zdroje v Delphi

Jak jsme si již uvedli v předchozích textech týkajících se zdrojů, obecně se zdroje vyskytují v binární podobě uvnitř souborů s příponami *.res. V Delphi lze o zdrojích hovořit ještě v jedné souvislosti: popis každého formuláře (třeba i s případnými ikonami apod.) je uložen v souboru s příponou *.dfm, který tedy také můžeme chápat jako určitý specifický typ zdrojů. Soubory *.dfm jsou od Delphi verze 5 ukládány v textové podobě, ale i tak mohou nést binární informaci o ikoně.

Vysvětlili jsme si také, jakým způsobem je realizován vztah program – zdroje. Řekli jsme si, že soubor se zdrojovým kódem a soubor se zdroji jsou na sobě relativně nezávislé, a že chceme-li připojit zdroje k programu, provedeme to v Delphi pomocí direktivy překladače $R. Výsledkem je přilinkování zdrojů k programovému souboru: vznikne tak jeden výsledný soubor *.exe, který obsahuje jak program a data, tak i zdroje. Potřebujeme-li změnit zdroje (a nahradit tak třeba ikonu programu nějakou jinou), můžeme v zásadě bez problémů použít nový soubor se zdroji <.res), ale nezbytnou podmínkou je opětovná kompilace programu.

K práci s některými druhy zdrojů (bitmapy, ikony a kurzory) v prostředí Delphi je určen pomocný nástroj Image Editor. Vrhněme se tedy do práce: nejprve si ukážeme, jak pomocí Image Editoru vytvořit soubor se zdroji a jak jej použít v aplikaci vytvořené v Delphi.

Krok jedna – spuštění Image Editoru

Začneme prací s ikonami. Spustíme tedy nástroj Image Editor. Můžeme to provést hned několika způsoby:

Krok druhý – vytvoření souboru se zdroji

Poté, co je Image Editor spuštěn, je nutné vytvořit nový soubor se zdroji: File – New – Resource File. Otevře se nový soubor (Untitled1.res), do něhož nyní vložíme několik ikon. Opakovaně tedy volte Resource – New – Icon. V dialogu, v němž je nutné zadat atributy ikony, zvolte Small Icon (16x16 pixelů) a ponechte standardní nastavení 16 barev, viz obrázek:

Potvrďte a nová ikona (zatím samozřejmě prázdná) je vložena do souboru Untitled1.res. Tímto způsobem vložte do souboru několik ikon, řekněme alespoň tři nebo čtyři (viz následující obrázek):

Nyní raději ikony přejmenujeme, protože názvy Icon1 až Icon4 vskutku nejsou příliš vypovídající. Protože mé výtvarné výtvory opravdu nejsou příliš reprezentativní, nepokusím se v následujícím příkladu kreslit nějaké složité grafické efekty a omezím se na geometrické útvary. Tomu tedy přizpůsobím názvy ikon. Připomenu, že přejmenování ikony je velmi jednoduché a provede se klepnutím na příslušný název pravým tlačítkem myši a zvolením položky „Rename“ z kontextové nabídky. Výsledek tedy může vypadat třeba takto:

Krok třetí – nakreslení ikon

Nyní zbývá poslední „maličkost“ – jednotlivé ikony nakreslit. Formálně jde o proces, který se odstartuje velmi jednoduše: stačí poklepat na název ikony a otevře se okno s jednoduchým grafickým editorkem (viz následující obrázek). V něm pak předáme slovo své umělecké můze a nakreslíme ikony tak, jak si přejeme, aby nakonec v aplikaci vypadaly.

Tak. Dejme tomu, že se nám úspěšně podařilo ikony nakreslit. Podotknu, že pokud nejste v malování zrovna dvakrát zruční, můžete použít třeba schránku systému Windows a s její pomocí zkopírovat do editoru třeba obsah jiné ikony nebo obrázku, který najdete někde na Internetu, nebo který získáte nějakou jinou cestou.

Jsou-li ikony nakresleny, stačí soubor Untitled1.res uložit (z nabídky Image Editoru File – Save). My jej při té příležitosti také přejmenujeme, třeba na NaseIkony.res. Po potvrzení můžete Image Editor opustit – jeho úloha v tomto okamžiku skončila.

Podíváte-li se nyní do vytvořeného souboru NaseIkony.res, příliš z jeho obsahu nezmoudříte, viz krátká ukázka:

        ˙˙  ˙˙                  (      ˙˙ ˙˙            (            Ŕ                        €  €  €€ €  € € €€  ŔŔŔ €€€  ˙  ˙  ˙˙ ˙  ˙ ˙ ˙˙  ˙˙˙                            ŞŞŞ    ŞŞŞŞ  ŞŞŞŞŞŞ
ŞŞŞŞŞŞ ŞŞŞŞŞŞŞ ŞŞŞŞŞŞŞ ŞŞŞŞŞŞŞ ŞŞŞŞŞŞŞ
ŞŞŞŞŞŞ  ŞŞŞŞŞ    ŞŞŞŞ                  ˙˙  ˙˙  ˙˙  ü  đ  Ŕ   €           €   Ŕ 

Na této ukázce tedy jasně vidíme, že výsledek je už hotovým souborem se zdroji (který je v binární formě, jak jsme si uvedli v předchozím, teoretickém pojednání o zdrojích). Jinak řečeno – tnto soubor již můžeme bez jakýchkoliv dalších úprav použít přímo v aplikaci. Zdůrazňuji to proto, že si později ukážeme ještě jednu možnost vytvoření zdrojů, a při ní se k použitelnému (binárnímu) souboru budeme muset teprve dopracovat.

Krok čtvrtý – vytvoření aplikace v Delphi

Nyní již můžeme spustit Delphi a vytvořit nový projekt. Naším cílem bude jednoduchá aplikace, která bude obsahovat seznam, z něhož bude moci uživatel zvolit požadovanou ikonu. Tato ikona se posléze zobrazí na formuláři.

Na formulář tedy vložte komponentu ListBox a Button. Ošetříme nejprve událost OnCreate formuláře. V obsluze této události pouze přidáme položky do seznamu (a nastavíme titulek formuláře a titulek tlačítka):

procedure TForm1.FormCreate(Sender: TObject);
begin
  ListBox1.Items.Add(`Ctverec`);
  ListBox1.Items.Add(`Trojuhelnik`);
  ListBox1.Items.Add(`Kruznice`);
  ListBox1.Items.Add(`Kruh`);

  Caption := `Ukazka prace se zdroji`;
  Button1.Caption := `Zmen ikonu`;
end;

Klíčová činnost bude obsažena v obsluze události OnClick tlačítka Button1. V obsluze této události musíme načíst zvolenou ikonu ze zdrojů. K tomu použijeme funkci Windows API LoadIcon:

HICON LoadIcon(
    HINSTANCE hInstance, // handle of application instance
    LPCTSTR lpIconName // icon-name string or icon resource identifier
  );

Funkce LoadIcon slouží k načtení ikony ze spustitelného souboru <.EXE). Jejím prvním parametrem je handle aplikace, jejíž spustitelný modul obsahuje ikonu, kterou chceme načíst. V našem případě se jedná přímo o naši aktuální (spuštěnou) aplikaci. Jak ji však identifikovat? Jak zjistíme potřebnou hodnotu handle?

Odpověď hledejme v jednotce SysUtils. Ta totiž obsahuje definici proměnné hInstance, která obsahuje právě potřebný handle. Přesněji řečeno – hInstance obsahuje hodnotu handle aktivní instance aplikace.

Druhým parametrem funkce LoadIcon říkáme, kterou ikonu chceme načíst. Identifikujeme ji jejím názvem, tak, jak jsme jej uvedli při vytváření souboru res v Image Editoru.

Návratovou hodnotou funkce LoadIcon je handle načtené ikony. Tento handle pak přiradíme do vlastnosti Icon.Handle, kterou máme k dispozici v Delphi. Výsledek, tedy fungující použití funkce LoadIcon, bude tedy vypadat takto:

procedure TForm1.Button1Click(Sender: TObject);
begin
  case ListBox1.ItemIndex of
    0 : Icon.Handle := LoadIcon(hInstance, `CTVEREC`);
    1 : Icon.Handle := LoadIcon(hInstance, `TROJUHELNIK`);
    2 : Icon.Handle := LoadIcon(hInstance, `KRUZNICE`);
    3 : Icon.Handle := LoadIcon(hInstance, `KRUH`);
    else ShowMessage(`Oznacte prosim pozadovanou ikonu v seznamu.`);
  end;
end;

Zdálo by se, že jsme hotovi. Zkuste aplikaci přeložit a spustit (F9): funguje?

Jako už tradičně, v okamžiku, kdy položím řečnickou otázku „Funguje?“, aplikace nefunguje. Sice jsme bez potíží provedli překlad, nicméně i když budeme za běhu zuřivě vybírat názvy ze seznamu a klepat na tlačítko, ikona formuláře se nezmění. Proč?

Pozorní čtenáři problém jistě odhalili: musíme učinit ještě jeden, velmi důležitý krok, bez něhož aplikace nemůže fungovat. Musíme totiž soubor se zdroji přilinkovat k našemu spustitelnému souboru. Ještě než to učiníme, prohlédneme si velikost spustitelného souboru. Přestože ikony zabírají (vzhledem ke své malé velikosti a pouze 16 použitým barvám) hodně málo bajtů na disku, drobný rozdíl postřehneme.

Nevím, jak u vás (záleží to na řadě věcí, například na verzi Delphi), ale v mém případě zabírá spustitelný soubor aplikace 324608 bajtů. Připomínám, že se nyní jedná o nefunkční verzi bez ikon. Nyní připojíme k aplikaci soubor s ikonami, který jsme společně vytvořili v první části dnešního článku. Pokud jste četli i předchozí články o zdrojích, určitě si vzpomenete, že k přilinkování zdrojů slouží direktiva $R, kterou připíšeme do úvodu zdrojového souboru, hned za řádku, která přilinkovává soubor se zdroji formuláře (DFM):

...
var
  Form1: TForm1;

implementation

{$R *.DFM}
{$R NaseIkony.res}

...

Důležité ovšem je, abyste soubor NaseIkony.res nahráli do téhož adresáře, ve kterém je nyní projekt v Delphi. Pokud necháte soubor NaseIkony.res jinde, překladač jej nenajde a ohlásí při překladu chybu.

Pokud nyní aplikaci přeložíme a spustíme, zaregistrujeme dvě odlišnosti:

Krok pátý – aktualizace aplikace

Aplikace funguje hezky, nicméně vzniká asi otázka, proč jsme to všechny dělali, neboli jaké jsou výhody tohoto řešení. Hlavní výhoda spočívá asi v tom, co se stane při požadavku na změnu ikon. Přesněji řečeno: co musíme udělat, budeme-li chtít nahradit současnou množinu čtyř ikon jinými? (další verze aplikace, lepší provedení ikon apod.)

Postup bude poměrně jednoduchý, navíc při něm poznáme výhody i nevýhody koncepce zdrojů. Nejprve spustíme Image Editor a vytvoříme nový soubor se zdroji, třeba NaseNoveIkony.res. Do něho přidáme čtyři ikony, pojmenujeme je stejně jako v předchozím případě (CTVEREC, TROJUHELNIK, KRUZNICE a KRUH). Nakreslíme vové, lepší ikony.

Soubor NaseNoveIkony.res, který jsme takto vytvořili, ovšem nestačí kamsi zkopírovat a očekávat, že aplikace si jej „načte“. Musíme provést rekompilaci projektu: otevřeme projekt v Delphi, napíšeme do direktivy $R nový název souboru (případně ponecháme stávající, pokud bychom název souboru se zdroji nezměnili) a aplikaci znovu zkompilujeme. Její integrální částí se tak stanou nové ikony – ale soubor s nimi (NaseNoveIkony.res) můžeme zase klidně smazat. Tímto způsobem tedy probíhá aktualizace aplikace – není nutné nijak zasahovat do zdrojového kódu (snad jen s výjimkou zadání názvu souboru *.res), ale opětovnému překladu se nevyhneme.

Novou verzi aplikace (s novou ikonou) vidíte na následujícím obrázku:

Zdrojový kód

Jako tradičně, i dnes si uvedeme kompletní zdrojový kód. V dnešním případě všakmusím zdůraznit, že abyste jej mohli bez problémů použít (a přeložit), je nutné vytvořit i soubor se zdroji NaseIkony.res, který bude při překladu přilinkován do výsledného spustitelného souboru EXE.

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

type
  TForm1 = class(TForm)
    ListBox1: TListBox;
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}
{$R NaseIkony.res}

procedure TForm1.FormCreate(Sender: TObject);
begin
  ListBox1.Items.Add(`Ctverec`);
  ListBox1.Items.Add(`Trojuhelnik`);
  ListBox1.Items.Add(`Kruznice`);
  ListBox1.Items.Add(`Kruh`);

  Caption := `Ukazka prace se zdroji`;
  Button1.Caption := `Zmen ikonu`;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  case ListBox1.ItemIndex of
    0 : Icon.Handle := LoadIcon(hInstance, `CTVEREC`);
    1 : Icon.Handle := LoadIcon(hInstance, `TROJUHELNIK`);
    2 : Icon.Handle := LoadIcon(hInstance, `KRUZNICE`);
    3 : Icon.Handle := LoadIcon(hInstance, `KRUH`);
    else ShowMessage(`Oznacte prosim pozadovanou ikonu v seznamu.`);
  end;
end;

end.

Na závěr

V dnešním článku jsme si vysvětlili, jak prakticky vytvořit soubor se zdroji, jak jej použít při psaní aplikace v Delphi a jak následně tento souboru aktualizovat. Práce s ikonami přitom patří k jednodušším aspektům tvorby zdrojů v Delphi. V dalším článku se proto budeme věnovat i jiným typům zdrojů, než jsou jen ikony.

Vaše ohlasy, které mi pravidelně zasíláte na emailovou adresu vkadlec@post.cz, a za které vám velmi děkuji, svědčí o tom, že problematika zdrojů vás zaujala a že ji považujete za dobře využitelnou při své praktické, každodenní práci. Věřím, že i dnešní článek přispěl k vaší lepší orientaci v této tématice a že pro vás bude užitečný.