ImageEn for Delphi and C++ Builder ImageEn for Delphi and C++ Builder

 

ImageEn Forum
Profile    Join    Active Topics    Forum FAQ    Search this forumSearch
 All Forums
 ImageEn Library for Delphi, C++ and .Net
 ImageEn and IEvolution Support Forum
 Best thread approach for populating images?

Note: You must be registered in order to post a reply.
To register, click here. Registration is FREE!

View 
UserName:
Password:
Format  Bold Italicized Underline  Align Left Centered Align Right  Horizontal Rule  Insert Hyperlink   Browse for an image to attach to your post Browse for a zip to attach to your post Insert Code  Insert Quote Insert List
   
Message 

 

Emoji
Smile [:)] Big Smile [:D] Cool [8D] Blush [:I]
Tongue [:P] Evil [):] Wink [;)] Black Eye [B)]
Frown [:(] Shocked [:0] Angry [:(!] Sleepy [|)]
Kisses [:X] Approve [^] Disapprove [V] Question [?]

 
Check here to subscribe to this topic.
   

T O P I C    R E V I E W
cpstevenc Posted - Apr 23 2013 : 09:13:12
Version 4.3.0

I have a deal where I hit up a database to that gives me a list of images and where to grab them.

Using a TImageEnMView.

I will traverse the database to get a list of file paths.

I will then start adding them to the TImageEnMView one by one.
If an image doesn't exist, i show a dummy image in its place.

If there is alot of images, and accessing a share, its slow.
Real slow.

SOOO, is there a preferred method of moving this action to a
back ground thread, to get a list of images into memory, all their text, colors, whatever done, then plop them into the TImageEnMView ?

The same would go for a TImageEnView , as I will have a "primary" image given to me which can be big-ish.. so like to load that up in background and then show it once loaded.

I was requested to try and keep the main UI lock up free while loading
these images from their sources.
11   L A T E S T    R E P L I E S    (Newest First)
cpstevenc Posted - Nov 13 2013 : 15:10:14
Thanks Ill check this out when I get a chance.. I didn't look it over much
but assume this will work with D7?
w2m Posted - Oct 01 2013 : 09:40:02
Over the summer I became more proficient writing Delphi threads with imageen so another alternative is shown below. I am not sure how this will work on a network or how many images you are loading, but in my testing here 50, 18.1mp. photographs are loaded nearly instantly and you do not loose access to the GUI.

This approach is similar to that suggested by Fabrizio, except the images are added to ImageEnMView with AppendImage from a stringlist containing the file paths in a thread. The speed is similar to FillFromDirectory because FillFromDirectory is threaded.

For a database application add the filenames from the database to the stringlist something like this:

{ Get the filenames from the database }
AStringList.Add(t.Item[x].Location);
Here I assume that the tImageDataCollection is already created and accessible to the thread. If the tImageDataCollection is not accessible in the thread then you will have to put the code in a Syncronize block to access it from the main form:
{ Get the file list from the database on the main form }
Synchronize(
procedure
begin
  AStringList.Add(Form1.t.Item[x].Location);
end);


Let me know if this works for you.

unit Unit1;
{$WARN SYMBOL_PLATFORM OFF}
{$WARN UNIT_PLATFORM OFF}

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
  System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
  imageenview, Vcl.ExtCtrls, iemio, ieview, iemview, Vcl.StdCtrls,
  Vcl.ComCtrls;

type

  { A TThread descendent for Loading an Image }
  TImageLoadingThread = class(TThread)
  private
    { Private declarations }
  protected
    { Protected declarations }
    procedure Execute; override;
  public
    { Public declarations }
    constructor Create(CreateSuspended: Boolean);
  end;

  TForm1 = class(TForm)
    ImageEnMView1: TImageEnMView;
    ImageEnMIO1: TImageEnMIO;
    Panel1: TPanel;
    Splitter1: TSplitter;
    ImageEnView1: TImageEnView;
    FillWithThread1: TButton;
    Folder1: TEdit;
    Browse1: TButton;
    FillFromFolder1: TButton;
    Fit1: TCheckBox;
    Clear1: TButton;
    StatusBar1: TStatusBar;
    procedure FormCreate(Sender: TObject);
    procedure Browse1Click(Sender: TObject);
    procedure FillFromFolder1Click(Sender: TObject);
    procedure FillWithThread1Click(Sender: TObject);
    procedure Clear1Click(Sender: TObject);
    procedure ImageEnMView1AllDisplayed(Sender: TObject);
    procedure Folder1Change(Sender: TObject);
    procedure ImageEnMView1ImageSelect(Sender: TObject; idx: Integer);
    procedure Fit1Click(Sender: TObject);
  private
    { Private declarations }
    AImageLoadingThread: TImageLoadingThread;
    procedure ThreadTerminate(Sender: TObject);
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses Vcl.FileCtrl, Winapi.ShlObj, Winapi.ShellApi, hyieutils, imageenio;

{ Global }

function DesktopFolder: string;
{ Find Desktop folder location }
var
  iR: Bool;
  iPath: array [0 .. MAX_PATH] of Char;
begin
  iR := Winapi.ShlObj.ShGetSpecialFolderPath(0, iPath, CSIDL_DESKTOP, False);
  if not iR then
    raise Exception.Create('Could not find Desktop folder location.');
  Result := IncludeTrailingPathDelimiter(iPath);
end;

function BrowseForFolder(AFolder: string): string;
{ BrowseForFolder }
begin
  if System.SysUtils.DirectoryExists(AFolder) then
    if Vcl.FileCtrl.SelectDirectory('Select Folder', 'Desktop', AFolder) then
      Result := AFolder;
end;

function GetAllFiles(AMask: string; AStringList: TStringList): Integer;
{ Get all supported image files matching mask (*.*.. '*.jpg') and add them to a stringlist }
var
  iSearch: TSearchRec;
  iDirectory: string;
  iCount: Integer;
  iFileAttrs: Integer;
begin
  iCount := 0;
  iDirectory := ExtractFilePath(AMask);
  iFileAttrs := faArchive - faHidden;
  { Find all files matching AMask }
  if FindFirst(AMask, iFileAttrs, iSearch) = 0 then
  begin
    repeat
      if IsKnownFormat(iDirectory + iSearch.name) then
      begin
        { Add the imageenio supported files to the stringlist }
        AStringList.Add(iDirectory + iSearch.name);
        Inc(iCount);
      end;
    until FindNext(iSearch) <> 0;
  end;
  { Subdirectories }
  if FindFirst(iDirectory + '*.*', faDirectory, iSearch) = 0 then
  begin
    repeat
      if ((iSearch.Attr and faDirectory) = faDirectory) and
        (iSearch.name[1] <> '.') then
        GetAllFiles(iDirectory + iSearch.name + '\' + ExtractFileName(AMask),
          AStringList);
    until FindNext(iSearch) <> 0;
    FindClose(iSearch);
  end;
  Result := iCount;
end;

{ TImageLoadingThread }

constructor TImageLoadingThread.Create(CreateSuspended: Boolean);
{ Create the thread. }
begin
  inherited;
end;

procedure TImageLoadingThread.Execute;
{ Load images ImageENMView in the thread.
  Note: All references to GUI elements must occur inside a Synchronize method.
  Synchronize suspends the current thread and has the main thread call the procedure. When
  the procedure finishes, control returns to the current thread. }
var
  i: Integer;
  iFolder: string;
  iFileCount: Integer;
  iStringList: TStringList;
begin
  inherited;
  { Free the thread onTerminate }
  FreeOnTerminate := True;

  if not Terminated then
  begin

    { Get the foldername }
    Synchronize(
      procedure
      begin
        iFolder := IncludeTrailingPathDelimiter(Form1.Folder1.Text);
      end);

    { Create a string list to hold the filenames }
    iStringList := TStringList.Create;
    try
      iFileCount := GetAllFiles(iFolder + '*.*', iStringList);
      if iFileCount > 0 then
      begin
        for i := 0 to iFileCount - 1 do
          { Add the images to ImageEnMView }
          Synchronize(
            procedure
            begin
              if iFileCount > 0 then
              begin
                Form1.ImageEnMView1.ImageFileName
                  [Form1.ImageEnMView1.AppendImage()] := iStringList.Strings[i];
              end;
            end);
      end
      else
        MessageBox(0, 'No supported images were found', 'Warning',
          MB_ICONWARNING or MB_OK);

    finally
      iStringList.Free;
    end;

  end;

  { Terminate the thread }
  Terminate;
end;

{ TForm1 }

procedure TForm1.Folder1Change(Sender: TObject);
{ Folder1Change }
begin
  StatusBar1.Panels[0].Text := Folder1.Text;
end;

procedure TForm1.FormCreate(Sender: TObject);
{ Set parameters }
begin
  ImageEnMView1.SetModernStyling(True, 225, 225);
  Folder1.Text := DesktopFolder;
end;

procedure TForm1.ImageEnMView1AllDisplayed(Sender: TObject);
{ ImageEnMView1AllDisplayed }
begin
  ImageEnMView1.SelectedImage := 0;
  StatusBar1.Panels[2].Text := 'Images: ' + IntToStr(ImageEnMView1.ImageCount);
end;

procedure TForm1.ImageEnMView1ImageSelect(Sender: TObject; idx: Integer);
{ ImageEnMView1ImageSelect }
var
  iFilename: string;
begin
  iFilename := ImageEnMView1.ImageFileName[idx];
  ImageEnView1.IO.LoadFromFile(iFilename);
  ImageEnView1.AutoFit := Fit1.Checked;
  if Fit1.Checked then
    ImageEnView1.Fit;
end;

procedure TForm1.Browse1Click(Sender: TObject);
{ Browse for a folder }
var
  iFolder: string;
begin
  iFolder := BrowseForFolder(DesktopFolder);
  Folder1.Text := IncludeTrailingPathDelimiter(iFolder);
end;

procedure TForm1.Clear1Click(Sender: TObject);
{ Clear }
begin
  ImageEnMView1.Clear;
  ImageEnView1.Clear;
  Caption := 'Fill ImageEnMView In a Thread';
  StatusBar1.Panels[1].Text := '';
  StatusBar1.Panels[2].Text := '';
end;

procedure TForm1.FillFromFolder1Click(Sender: TObject);
{ Fill ImageEnMView with fill from directory }
var
  iFilename: string;
begin
  if System.SysUtils.DirectoryExists(Folder1.Text) then
  begin
    ImageEnMView1.Clear;
    ImageEnView1.Clear;
    Caption := 'Fill ImageEnMView In a Thread- ' + Folder1.Text;
    ImageEnMView1.FillFromDirectory(Folder1.Text, -1, False, '', True, '',
      False, True);
    Screen.Cursor := crHourGlass;
    try
      ImageEnMView1.SelectedImage := 0;
      iFilename := ImageEnMView1.ImageFileName[0];
      ImageEnView1.IO.LoadFromFile(iFilename);
      if Fit1.Checked then
      begin
        ImageEnView1.AutoFit := True;
        ImageEnView1.Fit;
      end;
      ImageEnView1.Bitmap.Modified := False;
      ImageEnMView1.SelectedImage := -1;
    finally
      Screen.Cursor := crDefault;
    end;
  end
  else
    MessageBox(0, PChar('The folder ' + Folder1.Text + ' does not exist.'),
      'Warning', MB_ICONWARNING or MB_OK);
end;

procedure TForm1.FillWithThread1Click(Sender: TObject);
{ Fill ImageEnMView with a thread }
begin
  if System.SysUtils.DirectoryExists(Folder1.Text) then
  begin
    ImageEnMView1.Clear;
    ImageEnView1.Clear;
    Caption := 'Fill ImageEnMView In a Thread- ' + Folder1.Text;
    { Create the thread suspended }
    AImageLoadingThread := TImageLoadingThread.Create(True);
    { Set the threads OnTerminate event }
    AImageLoadingThread.OnTerminate := ThreadTerminate;
    { Start the thread }
    AImageLoadingThread.Start;
    { Processing will occur in the TImageLoadingThread.Execute event }
  end;
end;

procedure TForm1.Fit1Click(Sender: TObject);
{ Auto Fit }
begin
  ImageEnView1.AutoFit := Fit1.Checked;
  if Fit1.Checked then
    ImageEnView1.Fit;
end;

procedure TForm1.ThreadTerminate(Sender: TObject);
{ ThreadTerminate }
var
  iObject: TObject;
begin
  { Show a message if exception takes place in the thread }
  Assert(Sender is TThread);
  iObject := TThread(Sender).FatalException;
  if Assigned(iObject) then
  begin
    { Thread terminated due to an exception }
    if iObject is Exception then
      Application.ShowException(Exception(iObject))
    else
      ShowMessage(iObject.ClassName);
  end
  else
  begin
    { Thread terminated without an exception }
  end;
  { Free the AImageLoadingThread }
  AImageLoadingThread := nil;
end;

end.

William Miller
Adirondack Software & Graphics
Email: w2m@frontiernet.net
EBook: http://www.imageen.com/ebook/
Apprehend: http://www.frontiernet.net/~w2m/index.html
cpstevenc Posted - Sep 30 2013 : 18:28:41
Kinda revisiting this.

The OnWork event , when I try to populate the ImageENMView, it slows down the UI loading the image up.
Being the images for the setup are always on a network drive, its gonna be a bit of delay.

I am using the method of using the ImageENMView.AppendImage(myimage);

If I comment this out, the response in my UI is quick again, and even fixes another UI
issue of a drawing problem with another component.
cpstevenc Posted - Apr 25 2013 : 14:23:31
Sorry for the lack of details.. was on my way out and rushed things.

I am using a combo of both your ideas.
I am using the background worker.
I THINK locking the paint and then unlocking it fixed my problem. I have yet to repeat. But here is the code I have currently which so far has worked.

procedure Tfrm_FindAndSell_Inventory.ImageThreadWork(Worker: TBackgroundWorker);
var 
  errormsg: string;
  error: Boolean;
  t     : tImageDataCollection;
  x    : Integer;
  idx : Integer;
begin
  try
    iowriteln('ImageThread - Work');  // logs to a console that can be open for debugging 

    Image_Browser.LockPaint; // locks paint
    Image_Primary.LockPaint;

    image_Browser.Enabled := false; // disable user interactions
    Image_Primary.Enabled := false;

    Image_Browser.Clear; // clear our image containers
    Image_Primary.Clear;

    if Assigned(image_Browser) = false then // if somehow the image_Browser got killed exit
      Exit;

    if Assigned(lastitem) = false then  // if our grid record last item is not assigned then exit
      Exit;

    if lastitem.SearchPart_Data.GUID = '' then  // if the GUID is blank then exit
      Exit;

    FreeAndNil(lastImages); 

// Get a List of Images from our given GUID and store to the T object

    if cmImage.Get_PartImages(lastitem.SearchPart_Data.GUID, errormsg, t) then

      if t.Count > 0 then
      begin

        lastImages := t; 

        for x := t.Count - 1 downto 0 do
        begin

          if worker.CancellationPending then
          begin
            worker.AcceptCancellation;
            Exit;
          end;

          IDX := Image_Browser.AppendImage;
          Image_Browser.ImageID[idx] := X; // ID will store our item index ID in the list
           
	// checks if file exists
          if io.newFileExists(t.Item[x].ThumbLocation) then
	          begin
	            Image_Browser.ImageFileName[idx] := t.Item[x].Location;
	             end 
	                 else
	 	          begin // if did not exist show our Generic Image saying so
		            Image_Browser.SetImage(idx, Generic_Data.Images.Items[0].bitmap);
		          end;
          Sleep(10); // sleep a momment
        end;
      end
      else
      begin
        iowriteln('ImageThreadWork Error 1 : ' + ErrorMsg); // we had an error of some sort from the cmImage.Get_PartImages call
      end;


   if assigned(t) then
	begin  
	    idx := t.PrimaryImageIdx; // get an index of our lists "Primary Image"
	    if idx > -1 then	// if it was -1 then don't bother
	    begin
		      if io.newFileExists(t.Item[idx].WebLocation) then
		        image_primary.IO.LoadFromFile(t.Item[idx].WebLocation)   // Load image into Image_Primary container
		      else
		        Image_primary.Bitmap.Assign(Generic_Data.Images.Items[0].bitmap); // file did not exist so load generic Image saying so
	    end;
	end;
    lastImages := t;

  except
    on e:
    Exception do
    begin
      iowriteln('ImageThreadWork Error 2 : ' + E.Message);
    end;
  end;
end;

procedure Tfrm_FindAndSell_Inventory.ImageThreadWorkComplete(Worker: TBackgroundWorker;
  Cancelled: Boolean);
begin
  iowriteln('ImageThread - Complete');
  image_Browser.Enabled := true;
  Image_Primary.Enabled := true;

  Image_Browser.UnLockPaint;
  Image_Primary.UnLockPaint;

  image_browser.Update;
  image_primary.Update;
end;
w2m Posted - Apr 25 2013 : 13:37:43
I can not debug what you are doing because you do not show any code.
quote:
I used the background thread deal with great results.

Do you mean that the BackgroundWorker component worked great or did Fabrizio's code work great? If BackgroundWorker component worked "great" and that is causing an exception now, why did it work well before?

I have used the BackGroundWorker component to create thumbnails in a thread using ImageEnIO... but... I guess it is possible the thread in ImageEnMView may be conflicting with the BackgroundWorker component thread.

You could try setting ImageEnMView.EnableImageCaching := False in OnFormCreate which may eliminate a conflict.

Does Fabrizio's method work for you?
With Fabrizio's method, ImageEnMView.EnableImageCaching should be set to True.

William Miller
Adirondack Software & Graphics
Email: w2m@frontiernet.net
EBook: http://www.imageen.com/ebook/
Apprehend: http://www.frontiernet.net/~w2m/index.html
cpstevenc Posted - Apr 25 2013 : 12:35:40
I am getting an AV error that I am trying to track down.
Using the recommended thread component now..

address 00719A8D

Its in TImageENMView.DrawImage
mov eax,[eax+$04]

can't quite figure out why its happening ... its def caused by whats happening in the onwork event but seems to be outside my try/except as i can't catch it.

cpstevenc Posted - Apr 24 2013 : 20:40:56
I used the background thread deal with great results. I moved the whole database query routine and all into that, and works great. Thanks!

Fabrizio - Ill keep that in mind.
fab Posted - Apr 23 2013 : 23:13:57
You could try also (in a loop, of course...):

ImageEnMView1.ImageFileName[ImageEnMView1.AppendImage()] := 'image1.jpg';
ImageEnMView1.ImageFileName[ImageEnMView1.AppendImage()] := 'image2.jpg';
..etc...

TImageEnMView1 will load specified images using background threads.
You can know when all images to display (all visible images) are loaded handling OnAllDisplayed event.
w2m Posted - Apr 23 2013 : 11:03:38
I do not know anything in ImageEn that would lock it other than possibly the Enabled property... but you could enable or disable the menu items or buttons that someone could select until you are ready to allow editing.

William Miller
cpstevenc Posted - Apr 23 2013 : 10:48:35
Thanks, checking it out now.

The images are not super big.. ranging from 5k to ~300k .. but because of a
design of the system, images are stored on a network share. And the share can
be really slow for some people. Some use NetDrive to map drives remotely,
which makes it all the slower. And sometimes can have up to 50 + images. So pulling
the images over a slow method right now causes lots of delays while its pooling them.

Not my design, someone elses from way back, so I have to deal with it now.

Any recommendations on the work process to make sure while images are
being loaded, that someone can't do something in ImageEnMView to make it
something ill-wanted to happen?
w2m Posted - Apr 23 2013 : 09:33:47
The Delphi Area website has a free simple easy to use component called backgroundworker:

http://www.delphiarea.com/products/delphi-components/backgroundworker/

TBackgroundWorker component allows you to run an operation on a separate, dedicated thread. Time-consuming operations like downloads and database transactions can cause your user interface (UI) to seem as though it has stopped responding while they are running. When you want a responsive UI and you are faced with long delays associated with such operations, TBackgroundWorker component provides a convenient solution.

I hsve used this component. It is well written, small and convienent to use. Read the help file to find out how to use it. It is easy...

Quick Usage Summary

Initialize the Background Worker

procedure TForm.Button1Click(Sender: TObject);
begin
   // if worker thread is still processing cancel the thread
   if BackgroundWorker.IsWorking then
   begin
      // inform user we are cancelling the last operation
       StatusBar.SimpleText := 'Cancelling...';
       // cancel the operation
       BackgroundWorker.Cancel;
       // and wait for worker to stop
       BackgroundWorker.WaitFor;
   end;
   // clear the old thumbnails
   ClearThumbnails;
   // inform user we are working
   StatusBar.SimpleText := 'Creating thumbnails...';
   // create new thumbnails
   BackgroundWorker.Execute;
end;

The thread is executed here:
procedure TMainForm.BackgroundWorkerWork(Worker: TBackgroundWorker);
// Add your code to fill ImageEnMView here...
begin
end;

As far as loading a big image into ImageEnView is quite fast. How big is the image you are loading.... can you post a link to the image here for testing?

William Miller
Adirondack Software & Graphics
Email: w2m@frontiernet.net
EBook: http://www.imageen.com/ebook/
Apprehend: http://www.frontiernet.net/~w2m/index.html