Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations derfloh on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

TImage created at run-time, not displaying 1

Status
Not open for further replies.

KempCGDR

Programmer
Jan 10, 2003
445
GB
Hi, I'm having problems making a TImage display when it is created at run-time. I've used virtually identical code before and it worked, so there must be something really stupid that I'm missing.

I have a class that contains the TImage in question declared thus (I will use '...' to show that there is other unrealted code there):

Code:
unit newClasses;

...

type  SectorClass = class(TObject)
  public
     img:TImage;
     ...
  end;

...

constructor SectorClass.Create(AOwner: TComponent);
begin
   img := TEdit.Create(AOwner);

   with img do
   begin
      Name := 'imgSector';

      AutoSize := False;
      Stretch := True;
      Visible := True;

      Top    := 247;
      Left   := 160;
      Width  := 126;
      Height := 91;

      onClick := sectorClick;
   end;
end;

in newClasses.pas and added into the Uses clause of Main.pas. In Main.pas I create in instance of this class like this:

Code:
unit Main;

...

type
  TfrmMain = class(TForm)
    ...
  private
    { Private declarations }
    sectorList:SectorListClass;
  public
    { Public declarations }
  end;

...

procedure TfrmMain.FormCreate(Sender: TObject);
begin
   sector := SectorClass.Create(frmMain);
end;

It appears to be created fine (I added showmessage('done') to various parts of the code and they all triggered, so the code is being executed), but it just isn't displaying.

Any thoughts on this one as I am stumped.
 
and of course, it's meant to read:

Code:
constructor SectorClass.Create(AOwner: TComponent);
begin
   img := TImage.Create(AOwner);

dunno where the TEdit came from, it's not in the code I copied o_O

To clarify, that wasn't the problem.
 
It's not clear to me where you are, if you are, actually loading the picture into the TImage. So if no picture (e.g. face.jpg) is being loaded into TImage then nothing will be displayed. Or am I being totally stupid here?

Andrew
Hampshire, UK
 
One other thing. You haven't specified the parent of the image. You need to do this to signify which control contains your TImage component. If you don't specify the parent then the image won't be visible.

So your code should look something like:
Code:
constructor SectorClass.Create(AOwner: TComponent);
begin
 img := TImage.Create(AOwner);

 with img do 
  begin
   Name := 'imgSector';
   Parent := frmMain; 
   AutoSize := False;
   Stretch := True;
   Visible := True;

   Top    := 247;
   Left   := 160;
   Width  := 126;
   Height := 91;

   Picture.LoadFromFile ( 'c:\mypicture.bmp' );

   onClick := sectorClick;
  end;

Andrew
Hampshire, UK
 
Ok, answering in order:

1) Yeah, I probably should have mentioned that an image is loaded in straight away, sorry.

2) I tried setting the Parent property with no effect. As a side-note in case I managed to mess it up, I pass in AOwner even though SectorClass doesn't require an owner so I can give the TImage an owner. When I tried setting Parent to this it gave me a type mismatch (TWinControl / TComponent if I remember correctly) so I had to cast AOwner as a TWinControl to get it to compile. WOuld this have screwed it up at all?
 
I just re-read my part 2 and realised it's next to unreadable, so here's my easy version:

SectorClass doesn't require an owner as it is derived from TObject. I pass the constructor the name of the form the object belongs to so that the TImage can be given it as its owner. I had to do the casting mentioned above for the "Parent := " line for the TImage before the code would compile.

There, much easier to read (I hope).
 
I find it difficult to diagnose these kind of problems unless you supply (copy and paste) the actual code that is failing.

Andrew
Hampshire, UK
 
Ok, it's seen some modification since then because of a couple of things, but it's basically the same.

Here is the parts of newClasses.pas related to the problem:

Code:
unit newClasses;

interface

uses StdCtrls,ExtCtrls, Classes, sysUtils, Dialogs, Controls;

type

  SectorClass = class(TObject)
  public
     img:TImage;
     sectorName:String;
     sType:String;
     flags:Byte;
     value:Byte;
     abilities:String;
     procedure setSector(newName:String);
     procedure sectorClick(Sender:TObject);
     constructor Create(AOwner: TComponent; i:Integer);
     destructor Destroy; override;
  end;


  SectorListClass = class(TObject)
  public
     sector:array[1..5] of SectorClass;
     deployed:string[5];
     constructor Create(AOwner: TComponent);
     destructor Destroy; override;
  end;

implementation

uses Globals;


constructor SectorClass.Create(AOwner: TComponent; i:Integer);
begin
   img := TImage.Create(AOwner);

   with img do
   begin
      Name := 'imgSector' + intToStr(i);
      Parent := (AOwner as TWinControl);

      AutoSize := False;
      Stretch := True;
      Visible := True;

      Top    := 247;
      Left   := (i * 160) - 136;
      Width  := 126;
      Height := 91;

      onClick := sectorClick;
   end;

   loadDLLPic(img, 'Sys', 'Unavailable');
end;


destructor SectorClass.Destroy;
<< code >>


procedure SectorClass.setSector(newName:String);
<< code >>


procedure SectorClass.sectorClick(Sender:TObject);
<< code >>


constructor SectorListClass.Create(AOwner: TComponent);
var
   i:Integer;
begin
   for i := 1 to 5 do
      sector[i] := SectorClass.Create(AOwner, i);
end;


destructor SectorListClass.Destroy;
<< code >>

end.

Basically, you create an instance of SectorListClass and it should handle the creation of the 5 sectors. The code for the position of the TImages does give correct results as I tried it on a simple array of 5 TImages and they displayed perfectly (using identical code in the With block). loadDLLPic() is a procedure defined elsewhere that does work (well, I should hope so, I've been using it for almost a year now).

Main.pas is below:

Code:
unit Main;

interface

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

type
  TfrmMain = class(TForm)
    lstC1: TListBox;
    lstC2: TListBox;
    lstC3: TListBox;
    lstC4: TListBox;
    lstC5: TListBox;
    lstP1: TListBox;
    lstP2: TListBox;
    lstP3: TListBox;
    lstP4: TListBox;
    lstP5: TListBox;
    lstPComm: TListBox;
    lstCComm: TListBox;
    lstPDeck: TListBox;
    lstPDiscard: TListBox;
    lstCDiscard: TListBox;
    lstCDeck: TListBox;
    tmrClick: TTimer;
    lstFWCards: TListBox;
    lblSFWCards: TLabel;
    lblSComm: TLabel;
    procedure setGameParams(Sectors:String);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
    sectorList:SectorListClass;
  public
    { Public declarations }
  end;

var
  {}
  frmMain: TfrmMain;


implementation

uses Globals, Decks;

{$R *.DFM}


procedure TfrmMain.setGameParams(sectors:String);
var
   raceFile:TextFile;
   raceVar,sectorName:String;
   i:integer;
begin
   { We need to fetch the names of each sector and then
     load the appropriate images }

   AssignFile(RaceFile, 'Races.dat.');
   Reset(RaceFile);
   Repeat
      Readln(RaceFile, RaceVar);
   Until (RaceVar = sectors);
   For i := 1 to 5 do
   begin
      Readln(RaceFile, RaceVar);
      sectorName := Copy(RaceVar,2,Length(RaceVar)-1);
      sectorList.sector[i].setSector(sectorName);
   end;
   CloseFile(RaceFile);

   frmMain.show;
end;


procedure TfrmMain.FormClose(Sender: TObject; var Action: TCloseAction);
begin
   sectorList.free;

   Application.Terminate;
end;


procedure TfrmMain.FormCreate(Sender: TObject);
begin
   sectorList := SectorListClass.Create(frmMain);
end;

end.

If you can sort through all that then hopefully it'll be a bit clearer.
 
Using your newClasses unit (replacing <<code>> with begin end; and commenting out the uses Globals), I created a simple main unit that only had the FormCreate as follows:-
Code:
procedure TfrmMain.FormCreate(Sender: TObject);
begin
   sectorList := SectorListClass.Create(frmMain);
end;
and it worked perfectly. It displayed 5 (identical) images across the form.

The only bit of code that I don't have is the code for your loadDLLPic procedure so I replaced that line with
Code:
   img.picture.LoadFromFile ( 'c:\mysql\setup.bmp' );
which loads up the MySQL dolphin image.

So I suspect that the problem is a compatibility issue with loadDLLPic. Unless you supply the code for that I can't go any further.
.(img, 'Sys', 'Unavailable');

program that

Andrew
Hampshire, UK
 
k, loadDLLPic as requested:

Code:
procedure loadDLLPic(var img:TImage; Race,CardName:String);
var
   JPEGIn:TJPEGImage;
   Res:TResourceStream;
   LibraryID:THandle;
   PicID,i:Integer;
   Temp:PChar;
   strTemp:Array[0..255] of Char;
   Fail:Boolean;
begin
   For i := 1 to Length(Race) do
      if Race[i] = ' ' then Race[i] := '_';
   Temp := PChar(ExePath+'Images\'+Race);
   LibraryID := LoadLibrary(Temp); // load resource-dll
   If LibraryID <> 0 then
   begin
      Fail := False;
      PicID := -1;
      Repeat
         inc(PicID);
         If LoadString(LibraryID,PicID,strTemp,sizeof(strTemp)) = 0 then
            Fail := True;
         If strTemp > CardName then Fail := True;
      Until ((strTemp = CardName) or (Fail));
      If (not Fail) then
      begin
         inc(PicID, 100);
         Res := TResourceStream.CreateFromID(LibraryID, PicID, RT_RCDATA);
         try
            JPEGIn := TJPEGImage.Create;
            img.Picture.Graphic := JPEGIn.Create;
            img.Picture.Graphic.LoadFromStream(Res);
            JPEGIn.Free;
         finally
            Res.Free;
         end;
      end
      else
         LoadDLLPic(img,'Sys','Unavailable');
   end
   else
      // If this isn't there then all hell breaks loose
      LoadDLLPic(img,'Sys','Unavailable');
end;

Basically, it skims through the string list in the appropriate DLL resource (also containing jpeg images) until it hits the right name and then it adds 100 to the index of the string and uses that as the index of the jpeg to load (I've set it up so that the jpegs have an index 100 higher than their name in the string list).

This procedure has worked perfectly for me in every other case, so I don't really see how it could start failing now.

Oh yeah, and the note at the end is referring to the fact that if the image doesn't exist then it calls itself to load the Unavailable image into the TImage, but if it can't find that then it goes into a loop until the stack collapses. Part of the code in the splash screen checks to make sure that image does exist.
 
As I don't have your DLL I can't test the code properly. Can you create a c:\test.jpg file and try replacing your LoadDLLPic procedure with this:
Code:
procedure loadDLLPic(var img:TImage; Race,CardName:String);
var
   JPEGIn:TJPEGImage;
begin
  JPEGIn := TJPEGImage.Create;
  try
    img.Picture.Graphic := JPEGIn.Create;
    img.Picture.Graphic.LoadFromFile('c:\test.jpg' );
  finally
    JPegIn.Free;
  end;
end;
If this replacement code works then the fault lies in the DLL or your original loadDLLpic procedure.

Why is the procedure 'setGameParams(Sectors:String)' declared before the private in your TfrmMain and when is it being called ?

Do the images appear if only the FormCreate procedure is executed?



Andrew
Hampshire, UK
 
Still nothing :(


As for setGameParams, that's what I've always done to make it a procedure belonging to the class that the form is an instance of. Is it meant to be after the private? It is called by the form that the user sees before Main (and which also creates Main). I commented out the line in that procedure that calls setSector in the sector object in case that was interferring with the testing (it loads a new image into the TImage), but still nothing coming up.
 
setGameParams should be after the private or public but it is unlikely to be the cause of the problem.

The next step is to completely replace your newClasses unit with this one which does work. Keep a copy of your original of course.

I'm using Delphi 7 Pro on Windows XP Professional by the way.
Code:
unit newClasses;

interface

uses StdCtrls, ExtCtrls, Classes, sysUtils, Dialogs, Controls;

type
  SectorClass = class
  public
    img: TImage;
    sectorName: String;
    sType: String;
    flags: Byte;
    value: Byte;
    abilities: String;
    procedure setSector(newName: String);
    procedure sectorClick(Sender: TObject);
    constructor Create(AOwner: TComponent; i: Integer);
    destructor Destroy; override;
  end;

  SectorListClass = class
  public
    sector: array[1..5] of SectorClass;
    deployed: string[5];
    constructor Create(AOwner: TComponent);
    destructor Destroy; override;
  end;

implementation

uses Jpeg;

procedure loadDLLPic(var img: TImage; Race,CardName: String);
var
  JPEGIn: TJPEGImage;
begin
  JPEGIn := TJPEGImage.Create;
  try
    img.Picture.Graphic := JPEGIn.Create;
    img.Picture.Graphic.LoadFromFile('c:\test.jpg' );
  finally
    JPegIn.Free;
  end;
end;

constructor SectorClass.Create(AOwner: TComponent; i: Integer);
begin
  img := TImage.Create(AOwner);
  with img do begin
    Name := 'imgSector' + intToStr(i);
    Parent := AOwner as TWinControl;
    AutoSize := False;
    Stretch := True;
    Visible := True;
    Top    := 247;
    Left   := (i * 160) - 136;
    Width  := 126;
    Height := 91;
    onClick := sectorClick;
  end;
  loadDLLPic(img, 'Sys', 'Unavailable');
end;

destructor SectorClass.Destroy;
begin end;

procedure SectorClass.setSector(newName: String);
begin end;

procedure SectorClass.sectorClick(Sender: TObject);
begin end;

constructor SectorListClass.Create(AOwner: TComponent);
var
  i: Integer;
begin
  for i := Low(sector) to High(sector) do
    sector[i] := SectorClass.Create(AOwner, i);
end;

destructor SectorListClass.Destroy;
begin end;

end.


Andrew
Hampshire, UK
 
Ok, that didn't work, but I have found what the problem was. I decided to add frmMain to the auto-create list rather than having the form before create it. The images proceeded to show perfectly when it was shown. So my next question is what is the difference between the form being created automatically and the line

frmMain := TfrmMain.create(frmMain);

or

frmMain := TfrmMain.create(self);

(I tried both)?
 
And now a freaky thing, if I use my original newClasses.pas it now throws an exception going into frmMain.Show (when the form is shown obviously). Crazy thing is that there is no code in frmMain.Show, I just have the begin and end.
 
Ok, with a bit of tweaking it's all running nicely, but I still don't see what the difference between the creation techniques is. That was the problem all along, damn thing.
 
Pity you didn't mention that you weren't auto-creating the form before.

Your should use

frmMain := TfrmMain.Create(self);

But also where you refer to TComponent, you should actually be referring to TWinControl. So your constructor should look like this.
Code:
constructor SectorClass.Create(AParent: TWinControl; i: Integer);
begin
  img := TImage.Create(AParent);
  with img do begin
    Name := 'imgSector' + intToStr(i);
    Parent := AParent;
    AutoSize := False;
    Stretch := True;
    Visible := True;
    Top    := 247;
    Left   := (i * 160) - 136;
    Width  := 126;
    Height := 91;
    onClick := sectorClick;
  end;
  loadDLLPic(img, 'Sys', 'Unavailable');
end;
This seems to work (for me, anyway).

Andrew
Hampshire, UK
 
I've given you a star (or two) for your help, thanks.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top