HOME - - - - - - - TUTORIALS INDEX - - - - - - - - - - - - Other material for programmers

Delphi: Displaying Images / Accessing Files

This has good information, and a search button at the bottom of the page

Please don't dismiss it because it isn't full of graphics, scripts, cookies, etc!

Click here if you want to know more about the source and format of these pages.


This is only a draft, sorry. I ran out of time while working on it. The program is working and necessary information is complete here, but the explanations may be a little hard to follow in places. The whole code for the program does appear at the end of the page, however.

This is written for Delphi 2.

This tutorial starts by showing you how to display a bitmap on a form. It goes on to product a little learning aid which diosplays a bitmap and then gives you three buttons, each with a different label.

I wrote the program along the way to a program for learning pupils' names at the start of the school year. I'll have a folder with a bitmap of each pupil's face. The files will be named with the children's names. The program will display one photo and three filenames, e.g. Huey, Duey and Luey. When I click on the name of the child whose picture is showing, I'll get the next picture. The same prigram, with a different selection of bitmaps in the folder, could be used for learning anything that matches an image with a short name- countries by their shape, breeds of dog, names of colors, names of chemistry glassware.... etc! If you develop a set of bitmaps from images you are entitled to distribute, I'd be interested in discussing making them available to a wider audience.

=================
Start a new project. From the "Additional" tab of component pallette add an "Image" component to the project's form.

Set up folder with 4 bitmaps. (In a laer draft of htis, I will explain how.) Name them Huey.bmp, Duey.bmp, Luey.bmp, Other.bmp. Make them EASY to recognise... I just used "H", "D", "L" and "O". Easy-to-get "questions will speed your debugging work.)

Put three buttons under the image component. Name buAns1, buAns2, buAns3. (Digress on sizing, aligning nicely.

Put a "New Question" button on form, name it buNewQ

Now, for an initial quick check: Copy "Huey.bmp" to root of C:. Create BuNewQClick event handler:

image1.picture.loadfromfile('C:\Huey.bmp');

... and get program running. It should display Huey.

Now, to get away from having to work in root directory....

Add a DirectoryListBox and a FileListBox to the form from the System tab of the components pallette.

Access the Events tab of DirectoryListBox1; double click in the field for the OnChange event. Enter the following as the OnChange handler:

FileListBox1.Directory := DirectoryListBox1.Directory;

(explain why above)

Run program, just to see if you can now navigate backing store.

Set filelistbox1's mask property to *.bmp. (Image copmponents can also display metafiles (.WMF) and icons (.ICO). If you want to use those for this program, you can add the capability with few changes to what is presented here.)

Add a lable to your form. Name it laAvailImages. Add an event handler for FileListBox1Change which says....

laAvailImages.caption:='Available images: '+inttostr(FileListBox1.Items.Count);

Now as you navigate the backing store, you should see the number of *.bmp files in whatever folder you are looking at in the newly created label. Add a global variable (explain how-- iAvailFiles:integer under form's "private" section. While at it: Add sTmp:string; there (will need in a moment)) called iAvailFiles. Revise the FileListBox1Change event handler to say....

iAvailFiles:=FileListBox1.Items.Count; laAvailImages.caption:='Available images: '+inttostr(iAvailFiles);

Nearly there!

Add a FormCreate handler which says....

iAvailFiles:=FileListBox1.Items.Count;

Replace your simple, test, buNewQClick with

if iAvailFiles<4 then begin
showmessage('You need to be in a directory with more images');
end (*no ; here*)
else begin
sTmp:=(FileListBox1.Items[random(iAvailFiles)]);
image1.picture.loadfromfile(sTmp);
end; (*else*)
Now when you run the program, you should be able to see all of the images if you click the New Question button often enough. (They should come up in a random sequence. Add "Randomize" to the FormCreate handler to get a different "random" sequence each time you run the program. (That's not as daft as it seems, but a topic for another day!)

So that you don't have to continually re-navigate the directory, you can add

DirectoryListBox1.Directory:='C:\My Documents\Delphi\DD30\Bitmaps';

as the first line of the FormCreate event handler if...

a) Your bitmaps are in the same place on your disc as they are on mine, AND
b)You want to do something simple, but Really Dumb!

When the program is finished, you can set the "Start In" property of a shortcut to the program to the required directory. There's probably a better way to do what I wanted, but it doens't occur to me at the moment. You could use an .ini file, but that's going out of favor with Redmond. Not as bad as the hard coding that is the most easily understood solution!

So! Now you (should have) the essential hard bits in place: You can display randomly selected images on your form.

All that's left is to build the "test learner" elements of the program. Remember that in this simple approach, the correct "answer" for the name of any image is the image's filename. I.e. if we are learning the names of people, their images will be labelled with their names.

Add sAnwserIs:string to the global variables (along with sTmp and iAvailFiles)

Just after...

image1.picture.loadfromfile(sTmp);

... in buNewQClick, add
sAnswerIs:=sTmp;
buAns1.caption:=sTmp;
Now when you run the program and click on the New Question button, you should see the name of the displayed bitmap on the first of the three answer buttons. Assuming that your images will always be names SOMETHING.bmp, to strip off the ".bmp" part, you can modify what we just added and make it...
sAnswerIs:=copy(sTmp,1,length(sTmp)-4);
buAns1.caption:=sAnswerIs;
Now... what we have so far is not a very hard test! We need "distarctors", i.e wrong answers, and we should make it so that the right button to click is not always the first one!! (And we need to create handlers for the three answer buttons.

First we're going to make a procedure to set the answer buttons and underlying variables. First, name the form DD30. (30th Delphi Demo) (You can give it a name that suits you better, but if you do, there will be places in the following where you have to substitute your name for my "DD30".) Then enter...

procedure SetUpAnswerButtonsEtc;

... just after....

procedure FormCreate(Sender: TObject);

... in the form's type declaration.

Then, just after....
implementation
{$R *.DFM}
add something just to make sure the basics are okay:
procedure TDD30f1.SetUpAnswerButtonsEtc;
(*Before calling this procedure....
1) The folder the images are in must have
   been selected via DirectoryListBox1 and
   FileListBox1.
2) iAvailFiles must have been set.
3) sAnswerIs must have been set.
*)
begin
buAns1.caption:=sAnswerIs;
buAns2.caption:='Two';
buAns1.caption:='Three';
end;
Now replace the last line of buNewQClick (which at present should be "buAns1.caption:=sAnswerIs;") with....

SetUpAnswerButtonsEtc;

Now when you run the program and click the "New Question" button, the first button should become labelled with the name of the displayed bitmap, and the other two should become named "Two" and "Three".

Next we'll put some odd names on those other two buttons....

Rewrite SetUpAnswerButtonsEtc so that it becomes.....
procedure TDD30f1.SetUpAnswerButtonsEtc;
(*Before calling this procedure....
1) The folder the images are in must have
   been selected via DirectoryListBox1 and
   FileListBox1.
2) iAvailFiles must have been set.
3) sAnswerIs must have been set.
*)
begin
buAns1.caption:=sAnswerIs;
repeat
sTmp:=(FileListBox1.Items[random(iAvailFiles)]);
sTmp:=copy(sTmp,1,length(sTmp)-4);
until sTmp<>sAnswerIs;
buAns2.caption:=sTmp;
repeat
sTmp:=(FileListBox1.Items[random(iAvailFiles)]);
sTmp:=copy(sTmp,1,length(sTmp)-4);
until (sTmp<>sAnswerIs) and (sTmp<>buAns2.caption);
buAns3.caption:=sTmp;
end;
This is pretty crude, and it leaves the first button as always the right answer. When you are sure you see what is going on above, reprogram SetUpAnswerButtonsEtc again to make it the following, which is less pedestrian but more elegant, AND it "shuffles" the captions of the buttons so that the right button is not always the first button.
procedure TDD30f1.SetUpAnswerButtonsEtc;
(*Before calling this procedure....
1) The folder the images are in must have
   been selected via DirectoryListBox1 and
   FileListBox1.
2) iAvailFiles must have been set.
3) sAnswerIs must have been set.
*)
var sTmp2,sTmp3,sTmpTmp:string;
    c1:byte;
begin
buAns1.caption:=sAnswerIs;
repeat
sTmp2:=(FileListBox1.Items[random(iAvailFiles)]);
sTmp2:=copy(sTmp2,1,length(sTmp2)-4);
until sTmp2<>sAnswerIs;
repeat
sTmp3:=(FileListBox1.Items[random(iAvailFiles)]);
sTmp3:=copy(sTmp3,1,length(sTmp3)-4);
until (sTmp3<>sAnswerIs) and (sTmp3<>sTmp2);
sTmp:=sAnswerIs;
(*Shuffle (Could probably be improved!)*)
if random>0.5 then begin
   sTmpTmp:=sTmp;
   sTmp:=sTmp2;
   sTmp2:=sTmpTmp;
   end;
if random>0.5 then begin
   sTmpTmp:=sTmp;
   sTmp:=sTmp3;
   sTmp3:=sTmpTmp;
   end;
if random>0.5 then begin
   sTmpTmp:=sTmp2;
   sTmp2:=sTmp3;
   sTmp3:=sTmpTmp;
   end;
if random>0.5 then begin
   sTmpTmp:=sTmp;
   sTmp:=sTmp3;
   sTmp3:=sTmpTmp;
   end;
(*Shuffle finished*)
buAns1.caption:=sTmp;
buAns2.caption:=sTmp2;
buAns3.caption:=sTmp3;
end;
We're now going to create a single chunk of code to handle the clicking of any of the three answer buttons. It will need to check whether the right button was pressed, and respond accordingly. That will include generating a new question if the current question is answered correctly.

Before we can do that, we need the following....

Near the top of the sourcecode, in the type declaration, after...

procedure SetUpAnswerButtonsEtc;

... add ...

procedure ChooseOne;

After....
implementation
{$R *.DFM}
... add...
procedure TDD30f1.ChooseOne;
begin
end;
... BUT DON'T TRY TO COMPILE OR RUN YET.

First, copy everything from between the begin and end of buNewQClick to between the begin and end of ChooseOne, and replace everything from between the begin and end of buNewQClick with "ChooseOne".

Now to produce the event handler which takes care of a click on any of the Answer buttons. Start by going to the form and clicking on the first answer button.The hold down the shift key, and click on the other two answer buttons. You should find that all three are selected. You could also select them by dragging a selection box around them. Now click on the Events tab of the Object inspector and double click on the field to the right of the "OnClick" event. The system will assign a name of buAns1Click, and create the skelton of a handler in your source code. Fill it in as follows....

procedure TDD30f1.buAns1Click(Sender: TObject);
var sPrevRight:string;
begin
if sender=buAns1 then sTmp:=buAns1.caption;
if sender=buAns2 then sTmp:=buAns2.caption;
if sender=buAns3 then sTmp:=buAns3.caption;
(*I suspect that the previous three lines can be
 done more elegantly. sTmp:=sender.caption
 didn't work.*)
if sTmp=sAnswerIs then begin
sPrevRight:=sAnswerIs; (*Prevent same question arising twice in a row*)
repeat
ChooseOne;
until sAnswerIs<>PrevRight;
end;
end;
The name of that handler, buAns1Click, is misleading. DO NOT TRY TO EDIT THE NAME WITHIN THE SOURCECODE! Instead, go to the Object Inspector (press f11), get to the OnClick field for any of the answer buttons, e.g. buAns1, and edit the name in the OnClick field. All necessary changes will be made in your source code. (References to the old name, buAns1Click won't be altered, however, if they are in program comments.)

=======
The code we have produced could probably be tidied up, and could certainly be extended, but I hope it is sufficient to work in a simple way, and to have illustrated some things for you.

=======
As promised, here is the full listing of the finished program...
unit MGA14u1;

interface

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

type
  TMGA14f1 = class(TForm)
    Image1: TImage;
    buNewQ: TButton;
    FileListBox1: TFileListBox;
    DirectoryListBox1: TDirectoryListBox;
    laAvailImages: TLabel;
    meCopyright: TMemo;
    buCopyrightOK: TButton;
    Button1: TButton;
    BitBtn1: TBitBtn;
    BitBtn2: TBitBtn;
    BitBtn3: TBitBtn;
    procedure buNewQClick(Sender: TObject);
    procedure DirectoryListBox1Change(Sender: TObject);
    procedure FileListBox1Change(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure SetUpAnswerButtonsEtc;
    procedure ChooseOne;
    procedure buAllAnsClick(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure buCopyrightOKClick(Sender: TObject);
  private
    { Private declarations }
    iAvailFiles:integer;
    sTmp,sAnswerIs,sPrevRight:string;
  public
    { Public declarations }
  end;

var
  MGA14f1: TMGA14f1;

implementation

{$R *.DFM}

procedure TMGA14f1.ChooseOne;
begin
if iAvailFiles<4 then begin
BitBtn1.enabled:=false;
BitBtn2.enabled:=false;
BitBtn3.enabled:=false;
showmessage('You need to be in a directory with more images');
end (*no ; here*)
else begin
BitBtn1.enabled:=true;
BitBtn2.enabled:=true;
BitBtn3.enabled:=true;
repeat
sTmp:=(FileListBox1.Items[random(iAvailFiles)]);
image1.picture.loadfromfile(sTmp);
sAnswerIs:=copy(sTmp,1,length(sTmp)-4);
SetUpAnswerButtonsEtc;
until sAnswerIs<>sPrevRight;
end; (*else*)
end;

procedure TMGA14f1.SetUpAnswerButtonsEtc;
(*Before calling this procedure....
1) The folder the images are in must have
   been selected via DirectoryListBox1 and
   FileListBox1.
2) iAvailFiles must have been set.
3) sAnswerIs must have been set.
*)
var sTmp2,sTmp3,sTmpTmp:string;
    c1:byte;
begin
repeat
sTmp2:=(FileListBox1.Items[random(iAvailFiles)]);
sTmp2:=copy(sTmp2,1,length(sTmp2)-4);
until sTmp2<>sAnswerIs;
repeat
sTmp3:=(FileListBox1.Items[random(iAvailFiles)]);
sTmp3:=copy(sTmp3,1,length(sTmp3)-4);
until (sTmp3<>sAnswerIs) and (sTmp3<>sTmp2);
sTmp:=sAnswerIs;
(*Shuffle (Could probably be improved!)*)
if random>0.5 then begin
   sTmpTmp:=sTmp;
   sTmp:=sTmp2;
   sTmp2:=sTmpTmp;
   end;
if random>0.5 then begin
   sTmpTmp:=sTmp;
   sTmp:=sTmp3;
   sTmp3:=sTmpTmp;
   end;
if random>0.5 then begin
   sTmpTmp:=sTmp2;
   sTmp2:=sTmp3;
   sTmp3:=sTmpTmp;
   end;
if random>0.5 then begin
   sTmpTmp:=sTmp;
   sTmp:=sTmp3;
   sTmp3:=sTmpTmp;
   end;
(*Shuffle finished*)
BitBtn1.caption:=sTmp;
BitBtn2.caption:=sTmp2;
BitBtn3.caption:=sTmp3;
BitBtn1.font.color:=clBtnText;
BitBtn2.font.color:=clBtnText;
BitBtn3.font.color:=clBtnText;
end;

procedure TMGA14f1.buNewQClick(Sender: TObject);
begin
sPrevRight:=sAnswerIs;
ChooseOne;
end;

procedure TMGA14f1.DirectoryListBox1Change(Sender: TObject);
begin
FileListBox1.Directory := DirectoryListBox1.Directory;
end;

procedure TMGA14f1.FileListBox1Change(Sender: TObject);
begin
iAvailFiles:=FileListBox1.Items.Count;
laAvailImages.caption:='Available images: '+inttostr(iAvailFiles);
end;

procedure TMGA14f1.FormCreate(Sender: TObject);
begin
sPrevRight:='';
meCopyright.hide;
buCopyrightOK.hide;
iAvailFiles:=FileListBox1.Items.Count;
Randomize
end;

procedure TMGA14f1.buAllAnsClick(Sender: TObject);
begin
if sender=BitBtn1 then sTmp:=BitBtn1.caption;
if sender=BitBtn2 then sTmp:=BitBtn2.caption;
if sender=BitBtn3 then sTmp:=BitBtn3.caption;
(*I suspect that the previous three lines can be
 done more elegantly. sTmp:=sender.caption
 didn't work.*)
if sTmp=sAnswerIs then begin (*Act on right answer*)
sPrevRight:=sAnswerIs; (*Prevent same question arising twice in a row*)
repeat
ChooseOne;
until sAnswerIs<>sPrevRight;
end (*no ; here*)
else (*Act on wrong answer*)
begin
if sender=BitBtn1 then BitBtn1.font.color:=clred;
if sender=BitBtn2 then BitBtn2.font.color:=clred;
if sender=BitBtn3 then BitBtn3.font.color:=clred;
end;(*else*)
end;

procedure TMGA14f1.Button1Click(Sender: TObject);
begin
meCopyright.show;
buCopyrightOK.show;
end;

procedure TMGA14f1.buCopyrightOKClick(Sender: TObject);
begin
meCopyright.hide;
buCopyrightOK.hide;
end;

end.


   Search this site or the web        powered by FreeFind
 
  Site search Web search
Site Map    What's New    Search


Click here if you're feeling kind! (Promotes my site via "Top100Borland")
Ad from page's editor: Yes.. I do enjoy compiling these things for you... hope they are helpful. However.. this doesn't pay my bills!!! If you find this stuff useful, (and you run an MS-DOS or Windows pc) please visit my freeware and shareware page, download something, and circulate it for me? Links on your page to this page would also be appreciated!

Click here to visit editor's freeware, shareware page.


Link to Tutorials main page
Here is how you can contact this page's author, Tom Boyd.


Valid HTML 4.01 Transitional Page WILL BE tested for compliance with INDUSTRY (not MS-only) standards, using the free, publicly accessible validator at validator.w3.org


If this page causes a script to run, why? Because of things like Google panels, and the code for the search button. Why do I mention scripts? Be sure you know all you need to about spyware.

....... P a g e . . . E n d s .....