Page 1 of 1

dbPicShow - proper usuage

PostPosted: November 28th, 2010, 10:59 pm
by kidjake28
Hi everyone,

I'm fairly new to Delphi and I have to say that I love the PicShow components.

However I'm running into a problem that I KNOW is my programming.

I have about 9 dbpicshow components split between three timers. Each timer timer simply moves the corresponding adoTable.NEXT until it reaches the EOF and starts over again.

Eventually and intermittently my application will crash somewhere in the pseffect.pas code. Before stepping to the NEXT record i do a dbpicshow.stop on all components which I thought might be the problem.

I believe the problems is related to threading which I am not too familiar with. Can someone perhaps give me some insight as to how I should properly accomplish this task.

thank you

Re: dbPicShow - proper usuage

PostPosted: November 29th, 2010, 3:23 am
by kidjake28
This is where it crashes in pseffect.pas

Code: Select all
procedure Effect119(Display, Image: TBitmap; W, H, X, Y, Progress: Integer);
var
  dstPixel, srcPixel: PRGBQuad;
  Weight: Integer;
  I: Integer;
begin
  srcPixel := Image.ScanLine[H - 1];
  dstPixel := Display.ScanLine[H - 1];
  Weight := MulDiv(256, Progress, 100);
  for I := (W * H) - 1 downto 0 do
  begin
    with dstPixel^ do
    begin
      Inc(rgbRed, (Weight * (srcPixel^.rgbRed - rgbRed)) shr 8);        <-------------- Crashes here
      Inc(rgbGreen, (Weight * (srcPixel^.rgbGreen - rgbGreen)) shr 8);
      Inc(rgbBlue, (Weight * (srcPixel^.rgbBlue - rgbBlue)) shr 8);
    end;
    Inc(srcPixel);
    Inc(dstPixel);
  end;
end;

Re: dbPicShow - proper usuage

PostPosted: November 29th, 2010, 5:03 pm
by Kambiz
Obviously the problem is because of synchronization.

Calling Stop is not enough because the thread can be still working. You should use OnComplete event or check Busy property to know when the thread is actually stopped.

In other hand, having 9 threads is not efficient. It's better to use PicShow instances in manual mode, and use an external timer to advance Progress property of them manually.

Re: dbPicShow - proper usuage

PostPosted: November 29th, 2010, 6:36 pm
by kidjake28
Kazmbiz,

Thank you so much for the reply. I know it can be frustrating dealing with newbs (like myself) but I do appreciate it.

I understand the checking for busy and oncomplete event however I'm not very clear on using PicShow in manual mode.

First of all I guess I don't understand the implications of having 9 dbPicshows (consequently 9 threads). Will it slow the machine, use more memory? (I know newb question)

Secondly, are you saying that I configure all dbPicShows for manual mode, then when I advance to the next record, I start a timer or loop to advance the progress property?
Like this?

MainFRM.adodb1.Next;
MainFRM.adodb2.Next;
MainFRM.adodb3.Next;
for i = 1 to 100 do
begin
if not(dbPicShow1.busy) then dbPicShow1.Progress=i;
if not(dbPicShow2.busy) then dbPicShow2.Progress=i;
if not(dbPicShow1.busy) then dbPicShow3.Progress=i;
end;

BTW there is a THREADED property in dbPicShow, show this be enabled to disabled (or is it not related to what we are talking about ).

Re: dbPicShow - proper usuage

PostPosted: November 30th, 2010, 3:06 pm
by kidjake28
Any thoughts?

Re: dbPicShow - proper usuage

PostPosted: December 1st, 2010, 8:21 am
by Kambiz
The following blog post gives you an overview about the threads and their resource usages:
http://blogs.technet.com/b/markrussinov ... 61309.aspx

If you set Threaded property of PicShow to False, it uses a timer instead of thread to advance the transition. However, using 9 timers is worse than using 9 working threads.

By PicShow in manual mode, you can use a single timer to advance transitions on as many as PicShow controls. Here I briefly describe how to use PicShow in this way. I supposed DBPicShow controls are on your MainFRM.

  1. Set Manual property of all DBPicShow instances to True
  2. Drop a TTimer component on the form and name it TransTimer
  3. Set Interval property of the timer to 50 and its Enabled to False
  4. Use the following code for OnTimer event of the timer:

    Code: Select all
    procedure TMainFRM.TransTimerTimer(Sender: TObject);
    const
      Step = 2;
    begin
      // advance transitions
      for I := 0 to ControlCount - 1 do
        if Controls[I] is TDBPicShow then
           with TDBPicShow(Controls[I]) do Progress := Progress + Step;
      // stop timer if transitions are done
      TransTimer.Enabled := (dbPicShow1.Progress < 100);
    end;

  5. Use the following procedure to move to the next table record:

    Code: Select all
    procedure TMainFRM.NextImageSet;
    var
      I: Integer;
    begin
      // stop Transition timer
      TransTimer.Enabled := False;
      // stop all DBPicShow instances
      for I := 0 to ControlCount - 1 do
        if Controls[I] is TDBPicShow then
           TDBPicShow(Controls[I]).Stop;
      // advance to the next record
      adodb1.Next;
      adodb2.Next;
      adodb3.Next;
      // start all DBPicShow instances
      for I := 0 to ControlCount - 1 do
        if Controls[I] is TDBPicShow then
           TDBPicShow(Controls[I]).Execute;
      // start Transition timer
      TransTimer.Enabled := False;
    end;
Instead of TTimer, you can use a threaded timer (for example, TMultimediaTimer of Wave Audio Package).

Re: dbPicShow - proper usuage

PostPosted: December 2nd, 2010, 6:39 pm
by kidjake28
Thanks very much Kambiz,

With your example, I completely understand what you were referring to. Thanks for all the effort.

I have a few quick questions if you don't mind.

1) As for the 'procedure to move to the next table code'. I'd like to put this in a timer so that it waits a set amount a time before advancing to the next pic. Do I need to use a threaded timer for this or just the regular TTimer.

2) Why is is that you don't check .busy in your example? Do we not have to using this method?

3) I find that during the cycles some of the picture get missed (meaning it's doesn't display or it's 10% progress - light grey). What would cause the timer to not complete?

Re: dbPicShow - proper usuage

PostPosted: December 3rd, 2010, 8:59 am
by Kambiz
  1. You can use TTimer component.
  2. Both of Execute and Stop methods test Busy property internally. So, there is no need to explicitly check this property.
  3. Are you sure the images are not corrupted in the database? This may happen if images are in a string or memo field. To prevent this problem, either store images in a blob field or set Transliterate property of the string/memo field to False.

Re: dbPicShow - proper usuage

PostPosted: December 3rd, 2010, 12:34 pm
by kidjake28
1. Done.
2. Just curious as to why you asked me to check busy in the first post?
3. The fields are 'image' fields (mssql express). I import them as blobs. It doesn't happen to ALL the records and NOT the same ones. It's random as if the timer doesn't get the opportunity to complete, not sure. Keep in mind that I have THREE timers running. 7 of the dbPicshow on one, and the two other on separate timers. So I basically have 3 TTIMERS and 3 MultiMediaTimers. Could this be the problem?

Re: dbPicShow - proper usuage

PostPosted: December 3rd, 2010, 2:11 pm
by Kambiz
Regarding Busy property, that time DBPicShow was in asynchronous mode. In manual or non-threaded modes when we call Stop method, the transition is really stopped when the function returns. But in the threaded mode, the transition may stop later and in a non-predictive time.

Probably the images are corrupted in the database. Save one of those images from database to a file to see whether the image is corrupted or the problem is because of something else.

I don't know how you are using the MultimediaTimers, but it may be the reason of the problem. When accessing shared objects/variables inside a multimedia timer, you have to synchronous your thread with other threads (including main VCL thread) that access the same object/variable.

Re: dbPicShow - proper usuage

PostPosted: December 3rd, 2010, 2:41 pm
by kidjake28
Again thanks very much for the replies.

I understand the busy property now. I had forgotten that we switched the dbPicshow's to manual mode.

I exported the images as you said and their fine. I'm pretty sure your right about the timers and not synchronizing. You see I'm an old school DOS Basic tinkerer. Having to wrap my head around threading is new for me. Can you summarize what I should/should not be doing? I realize you may not have the time or patients but if you could point me to an article I would great appreciate it.

This is my set up

1st timer interval 5 seconds (stops 7 dbPicshows advances the records and enables the 1st MultimediaTimer which disables progress<100)
2nd timer interval 15 seconds (stops 1 dbPicShow advances the record and enables the 2nd MultmediaTimer which disables after progress<100)
3rd timer interval 5 minutes (stops 1 dbPicShow advances the record and enabled the 3rd MultimediaTimer which disables after progress<100)

I'm pretty sure that there is no synchronizing going on simply for the fact that I don't know how to do it or what is needed to synchronize.

I just noticed that the Jedi Timer is a threaded timer and one of the properties is syncEvent (I've searched high and low for jedi documentation but can't find anything). Will this help me?

Re: dbPicShow - proper usuage

PostPosted: December 4th, 2010, 6:53 am
by Kambiz
Here it is: Multithreading - The Delphi Way.

I am sure you will enjoy reading it.

Re: dbPicShow - proper usuage

PostPosted: December 4th, 2010, 9:43 pm
by kidjake28
Not sure if your being sarcastic but "I will enjoy reading it".

Thanks so much for everything. I'll let you know how I make out.

Re: dbPicShow - proper usuage

PostPosted: December 5th, 2010, 6:29 am
by Kambiz
I was serious.
Although I already knew about threading and the synchronization methods, but when I read this guide I enjoyed it a lot.