Tutorial-Ressorce einbinden

Durch das Stöbern im Internet, ist dem Autor dieser Seite mal aufgefallen wie vielfältig und unterschiedlich immer noch versucht wird Ressourcen in Lazarus einzubinden mit zum Teil veralteten aber leider auch haarsträubenden Konstruktionen. Ja, Lazarus ist manchmal kompliziert weil es auf mehreren Plattformen laufen kann. Ich werde hier auch nur eine Anleitung für Windows-Benutzer geben können, da ich nur mit diesem System vertraut bin. Hier habe ich aber auch sehr komplizierte Anleitungen gefunden die so nicht nötig sind. Ich beschreibe hier nur die für mich am einfachste Anleitung.

Vorbereitung

Erstellen sie eine neue Anwendung. Speichern Sie das Projekt in einem Neuen Ordner, den Sie erstellt haben ab. Am Besten erstellen Sie einen Unter-Ordner der Bilder oder so heißt. Besonders wichtig wenn sie mehrere Ressourcen einbinden wollen, denn die sind zusätzliche Dateien die bei jedem Compilieren eingebunden werden. Fehlen daraus Teile, dann fehlen die Teile in Ihrem fertigen Programm. Wenn Sie keine Ahnung haben was eine Anwendung ist dann machen sie erst das Tutorial Anwendung erstellen.

Einbinden einer PNG-Grafik.

Speichern Sie jetzt Ihr xxxxx.png in das Projekt oder Ihren Unter-Ordner.

Lazarus Projekteinstellungen wählen.
Lazarus-Projekt-Projekteinstellungen

Gehen Sie nun mit der Maus auf Projekt- Projekteinstellungen. Profis können auch Umschalt – Strg halten und F11 Tippen 🙂 . Sie befinden sich nun In den Projekteinstellungen. Und wie Sie hier sehen gibt es hier sehr viel einzustellen. Das folgende Bild zeigt die linke obere Ecke des Fensters.

Lazarus-Projekteinstellungen-Ressourcen
Lazarus-Projekteinstellungen-Ressourcen

Gehen Sie nun in dem Fenster Projekteinstellungen und klicken Sie auf Ressourcen. Auf der rechten oberen Seite des Fensters ändert sich Ihr Menü. Und es Sieht aus wie in dem folgenden Bild.

Lazarus - Ressource - Hinzufügen
Lazarus – Ressource – Hinzufügen

Und da jetzt „Löschen“ und „Alle Löschen“ nicht geht. (grau = inaktiv) Bleibt uns ja quasi nichts anderes übrig, als auf „Hinzufügen“ zu klicken. Schon öffnet sich das nächste Fenster.

Ein png- Symbol Bild Kussgesicht
Kussgesicht.png

Dieses Fenster ist ein Standard-Dialog-Fenster für Bilder welches Sie auch selber in Ihren Programmen benutzen können. Wählen Sie nun zunächst den passenden Ordner aus und klicken Sie dann auf das Bild welches Sie einbinden wollen. Ich habe mein Bild mir dem Namen „Kussgesicht.png“ im Unterordner „Bilder“ meines Projektes im Ordner „res4“ gespeichert und das wähle ich jetzt halt an.

Ressource wird in Lazarus angezeigt.
Kussgesicht.png ist geladen.

Wenn ich das gemacht habe, komme ich also in das vorherige Ressourcen-Fenster. Hier Klicke ich nun auf den Eintrag „Kussgesicht.png“. Nun wird rechts der Typ und der Name der Ressource angezeigt. Wie man unschwer erkennen kann, Ist dies der Name meiner Datei in Großbuchstaben und ohne Suffix (Endung hier .png) Lassen Sie den Typ auf „RCDATA“. Sie dürfen nun das Fenster mit der „OK-Taste“ schließen.

Quelltext für Ressource-Datei PNG

Vorbereitung: Erstellen sie eine IDE Anwendung und ziehen Sie einen TButton und ein Timage auf die Form. Binden Sie die Unit Windows mit der Uses-Klausel in die Anwendung ein. Doppelklicken sie auf den Button und ersetzen Sie das Ereignis durch folgenden Quelltext:

procedure TForm1.Button1Click(Sender: TObject);
var
  Speicherplatz: TResourceStream;
begin
  //Weise dem Speicherplatz mein Kussgesicht vom Typ RCDATA zu
  Speicherplatz := TResourceStream.Create(HInstance,
    'KUSSGESICHT', RT_RCDATA);
  //Die Ressource kann jetzt von dem Objekt Image aufgenommen werden.
  //Aber nur von Image / Imagelist /  TDBImagelist
  image1.Picture.LoadFromStream(Speicherplatz);
  Speicherplatz.Free;
end;

Wenn Sie alles nach Anleitung gemacht haben, müssten Sie Erfolg haben. Wenn sie diese Fehlermeldung haben:

unit1.pas(43,20) Error: Identifier not found "RT_RCDATA"

Dann haben Sie vergessen die Unit Windows einzubinden. So blöd es Klinkt diese benötigen Sie für „RT_RCDATA“. Wenn Sie folgende Fehlermeldung haben, obwohl sie die Unit Windows eingebunden haben.

unit1.pas(43,20) Error: Identifier not found "RCDATA"

Dann haben Sie vielleicht auch so wie ich zu Anfang den Ressourcen-Typ aus dem Fenster der Projekteinstellungen kopiert. Dies funktioniert sehr gut für den Ressourcen-Namen in meinem Beispiel also „KUSSGESICHT“, aber es funktioniert nicht für RCDATA weil es hier RT_RCDATA heißt. Und wenn Sie diese Fehlermeldung haben.

Error: Datei "Bilder\kussgesicht.png" nicht gefunden.

Dann haben Sie die Datei aus dem Ordner gelöscht oder ausgeschnitten ,verschoben oder umbenannt. Deswegen sollen diese Dateien im Projekt-Ordner als Unter-Ordner gespeichert werden. Hier ist das jetzt nur ein Bild welches das Programm nicht unbedingt zum Absturz bringt, aber Ressourcen können vielfältig und mal wirklich „systemrelevant“ sein. 🙂

Es gibt aber noch einen Fehler der nicht als solcher angezeigt wird. Wenn sie eine Ressource eingebunden haben und dann nicht vor dem Kompilieren speichern , kann es Vorkommen, dass Ihre Ressource nicht eingebunden wurde. Was der Compiler überhaupt nicht mag ist, dass sie bei einem laufenden Programm, also im Debug-Modus an den Projekt-Einstellungen rumfummeln. Wenn Sie das trotzdem gemacht haben, dann können Sie durch „alles Löschen“ und neu „Hinzufügen“ wieder Ordnung schaffen. Wenn sie danach vor dem Kompilieren alles Speichern dann ist alles wieder gut. Okay noch mal zum Quelltext den, muß ich auch noch erläutern.

procedure TForm1.Button1Click(Sender: TObject);
var
  Speicherplatz: TResourceStream;
begin
  //Weise dem Speicherplatz mein Kussgesicht vom Typ RCDATA zu
  Speicherplatz := TResourceStream.Create(HInstance,
    'KUSSGESICHT', RT_RCDATA);
  //Die Ressource kann jetzt von dem Objekt Image aufgenommen werden.
  //Aber nur von Image / Imagelist /  TDBImagelist
  image1.Picture.LoadFromStream(Speicherplatz);
  Speicherplatz.Free;
end;

Mit Speicherplatz mach ich mir ein Plätzchen für das Bildchen. Interessant ist ,dass ich hier keine Pfadangaben oder so machen brauche. Die Datei wird vom Compiler direkt in eine Ressourcen-Datei umgewandelt und dann gelinkt. Ich brauche dann nur noch den Namen „KUSSGESICHT“ Das ist jetzt keine Datei sondern ein Speicherbereich also kein .png oder .bmp etc. Jetzt kann ich das PNG-Bild in eine Image Ressource laden. Versuchen sie das bitte nicht mit einer Paintbox, das wäre so als müsste eine Katze ein Kälbchen gebären. Wenn das PNG-Bild erstmal in der Image ist, kann ich den Memory wieder freigeben. Nur wenn die Bilder sich in dem Image abwechseln können wäre es natürlich sinnvoller den Speicher solange zu reservieren bist das Programm zu Ende ist. Hier gehört das „Speicherplatz.Free;“ in die Close-Anweisung der Anwendung. Dann muß allerdings auch die Variable: „Speicherplatz: TResourceStream;“ Global definiert werden.

Hintergründe – Technik

Jetzt haben wir eine Ressource eingebunden, aber wir haben nicht im Quelltext stehen was auf uns eine Information zur Einbindung hindeutet. Wir haben also nicht mit der Compiler-Directive $R Irgendwas eingebunden. Dies steckt alles in unserer Anweisung, welches wir über Projekt-Projekteinstellungen-Ressourcen gegeben haben. Das ist zum einen bequem zum andern auch unübersichtlich. Wir sollten nämlich genau hinschauen, wenn wir mehrere Ressourcen im Programm haben. Wichtig ist hier, dass wir rechtzeitig überflüssige oder alte Dateien rechtzeitig löschen. Sonst schleppen wir noch mehr Müll mit uns rum, als ohne hin schon. Als Nachteil der bequemen objektorientierte Programmierung. Zum Thema Müll habe ich noch was. Wenn sie die anderen Funktionen der „Windows-Unit“ nicht brauchen, dann können Sie auf diese auch verzichten. Allerdings müssen Sie eine kleine Änderung an Ihrem Aufruf vornehmen.

procedure TForm1.Button1Click(Sender: TObject);
var
  Speicherplatz: TResourceStream;
begin
  //Weise dem Speicherplatz mein Kussgesicht vom Typ RCDATA zu
  Speicherplatz := TResourceStream.Create(HInstance,
    'KUSSGESICHT',Pchar(10));
  //Die Ressource kann jetzt von dem Objekt Image aufgenommen werden.
  //Aber nur von Image / Imagelist /  TDBImagelist
  image1.Picture.LoadFromStream(Speicherplatz);
  Speicherplatz.Free;
end;

Entscheidend ist hier die geänderte Zeile:

  Speicherplatz := TResourceStream.Create(HInstance,
    'KUSSGESICHT',Pchar(10)); 

RT_RCDATA ist ja nichts anderes als eine Konstante vom Typ Pchar. Diese Konstante ist Teil der Datei „Defines.inc“. Diese stellt verschiedene Windows Konstanten bereit. Natürlich erstellen die großartigen Entwickler von Lazarus diese Konstanten nicht nur aus Langeweile. Immerhin ist die Lesbarkeit durch die Bezeichnung RT_RCDATA extrem erhöht. Jedenfalls sagt diese Konstante mehr aus als „Pchar(10)“. Sie können andere Ressorcen ohne die folgende Liste gar nicht bereitstellen.

    RT_CURSOR       = MAKEINTRESOURCE(1);
    RT_BITMAP       = MAKEINTRESOURCE(2);
    RT_ICON         = MAKEINTRESOURCE(3);
    RT_MENU         = MAKEINTRESOURCE(4);
    RT_DIALOG       = MAKEINTRESOURCE(5);
    RT_STRING       = MAKEINTRESOURCE(6);
    RT_FONTDIR      = MAKEINTRESOURCE(7);
    RT_FONT         = MAKEINTRESOURCE(8);
    RT_ACCELERATOR  = MAKEINTRESOURCE(9);
    RT_RCDATA       = MAKEINTRESOURCE(10);
    RT_MESSAGETABLE = MAKEINTRESOURCE(11);

    DIFFERENCE = 11;

    RT_GROUP_CURSOR = MAKEINTRESOURCE(ULONG_PTR(RT_CURSOR) + DIFFERENCE);
    RT_GROUP_ICON = MAKEINTRESOURCE(ULONG_PTR(RT_ICON) + DIFFERENCE);
    RT_VERSION    = MAKEINTRESOURCE(16);
    RT_DLGINCLUDE = MAKEINTRESOURCE(17);
    RT_PLUGPLAY   = MAKEINTRESOURCE(19);
    RT_VXD        = MAKEINTRESOURCE(20);
    RT_ANICURSOR  = MAKEINTRESOURCE(21);
    RT_ANIICON    = MAKEINTRESOURCE(22);
    RT_HTML       = MAKEINTRESOURCE(23);
    RT_MANIFEST   = MAKEINTRESOURCE(24);

Die Frage erübrigt sich, wie bereits erwähnt sowieso wenn Sie, auf andere Funktionen der Windows API zurückgreifen wollen. Bedenken Sie jedoch, je mehr Sie von dieser Unit benutzen, desto schwieriger wird später die Portierung auf ein anderes System. Wie zum Beispiel auf das auch sehr gute Betriebssystem Linux.

Jetzt bleibt ja eigentlich nur noch eine Frage zu klären. Wenn ich das Programm – Lazarus beende und nach einiger Zeit neu starte, dann weiß der Compiler wieder was ich eingebunden habe. Komisch. Die Information steht wie bereits erwähnt weder in meiner Unit noch im Quelltext der Projekt – Datei. Also wo ist diese Information? Wie sie es bereits richtig erahnen, stelle ich hier keine Fragen wenn ich die Antwort nicht parat hätte.

Schauen wir uns die Dateien in unserem Ordner an. Zwischenzeitlich habe ich das kleine Bild, welches ich vorher hatte durch ein 700 kb Bild ersetzt. Damit wir die Unterschiede besser sehen können. Es ist diesmal ein JPG-Bild welches ich, außer der Änderung des Namens der Ressource ohne große Änderungen übernehmen konnte. Schauen wir uns die Dateien an die Lazarus nach dem nochmaligem kompilieren, ohne und mit den Ressourcen erzeugt.

Im Dateiexplorer kan man an der Größe die Ressource erkennen.
Links: ohne Ressource —– Rechts: mit Ressource

Neben der Exe-Datei ist noch eine zweite Datei angewachsen. Es ist, wie sollte es anders sein, die Ressourcen-Datei *.res hier liegt also das Bild als Information vor. Das ist also auch der Grund, warum wir die Ressourcen-Datei nicht extra einbinden müssen. Da das Bild ein Bestandteil der Standard-Ressourcen-Datei ist.Hier hat Sie den Namen „Projekt1.res“ weil ich das Projekt noch nicht umbenannt habe. Wenn ich mein Projekt einen anderen Namen gegeben habe, würde auch die Ressourcen-Datei den Namen übernehmen. Als letztes wäre jetzt noch zu klären wo die Anweisung zum Kompilieren denn jetzt steht.

Die Lazarus XML-Projekt-Datei
Lazarus XML-Projekt-Datei

In der Lpi-Projekt-Datei welches freundlicherweise eine gut nachzuvollziehende XML-Datei ist. Hier habe ich den entscheidenden Teil eingerahmt. In dem Rahmen sehen wir endlich die Verknüpfung zwischen der Datei und der Ressource. In der Roten Klammer sehen sieh den gesammten Abschnitt. Welches den Ressourcenteil einleitet mit der Anzahl der Einzel-Ressourcen hier count1. Und wie bei XML-Dateien üblich, das Ende mit einem /Schrägstrich gekennzeichnet wird.