Go Premium for a chance to win a PS4. Enter to Win

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 281
  • Last Modified:

How to force listening to buttonclick

Hi,

In my application I do some fast processing. When the processing starts I want to be able to pause it.
For this purpose I have added a pause-button to the form. The only thing is, that when I press the button it doesn't react immediately. Sometimes I have to press it multiple times before it works. Meanwhile processing takes on.
Is there a way to make sure that the buttonclick will always be noticed and processing of the job is paused immediately?
0
Stef Merlijn
Asked:
Stef Merlijn
  • 5
  • 4
  • 2
  • +1
2 Solutions
 
jimyXCommented:
It depends on what you are doing, and what easy changes to be made to catch the button OnClick.

From top of my mind i can say use "Application.ProcessMessages" to process the button's code. But you need to use with care as it enforces processing all the queued messages.
0
 
Stef MerlijnDeveloperAuthor Commented:
I'll try to use it and see what happens.
0
 
Sinisa VukCommented:
you can use threaded job to accomplish this. (Like BMThread). Thread will not stop your app and it can be easily stopped/paused.
0
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
Geert GruwezOracle dbaCommented:
here is a sample on how to use threading with a slow frontend
http://www.experts-exchange.com/Programming/Languages/Pascal/Delphi/A_6613.html

to add pausing and resuming you need access to thread instance.
changing the procedure into a function solves that
function LoadData(obj: TObject; DataLoaded: TDataLoadedProc): TLoadDataThread;
begin
  Result := nil;
  if not mLoadDataThreadRunning then
  begin
    mLoadDataThreadRunning := True;
    Result := TLoadDataThread.Create(Obj, DataLoaded);
  end;
end;

Open in new window


off course you need to save the result of that function in your form instance
  TfrmEEThreads = class(TForm)
  ... 
  private
    fLoadingThread: TThread;
  end;

procedure TfrmEEThreads.btnShowDataClick(Sender: TObject);
begin
  // Call procedure and pass grid to load and proc to load the grid
  fLoadingData := LoadData(sgData, ShowData);
end;

Open in new window

the button would simply do pause/resume the thread
procedure TfrmEEThreads.btnPause(Sender: TObject);
begin
  if (fLoadingData <> nil) then 
    fLoadingData.Pause;
end;

procedure TfrmEEThreads.btnResume(Sender: TObject);
begin
  if (fLoadingData <> nil) then 
    fLoadingData.Resume;
end;

Open in new window

0
 
Stef MerlijnDeveloperAuthor Commented:
Thank you all for the support.
I'll went for the approach suggested by Geert Gruwez. The posted article is great Geert.
0
 
Stef MerlijnDeveloperAuthor Commented:
@Geert Gruwez:
Using your example: I've tried to change the procedure into a function, but I get an error.
[DCC Error] uEELoadData.pas(35): E2065 Unsatisfied forward or external declaration: 'TLoadDataThread.LoadData'

Can you point me in the right direction?

unit uEELoadData;

interface

uses Classes;

const
  WaitSleep = 500;

type
  TDataLoadedProc = procedure (obj: TObject; List: TStrings) of object;
  TDataProgressProc = procedure (obj: TObject; ProcentDone: integer) of object;

implementation

uses SysUtils;

var
  mLoadDataThreadRunning: boolean;

type
  TLoadDataThread = class(TThread)
  private
    fDataLoaded: TDataLoadedProc;
    fDataProgress: TDataProgressProc;
    fObj: TObject;
    fList: TStrings;
    fProcentDone: integer;
    procedure DoDataLoaded;
    procedure DoProgress;
    function LoadData(obj: TObject; DataLoaded: TDataLoadedProc; DataProgress: TDataProgressProc): TLoadDataThread;
  protected
    procedure ReportProgress(ProcentDone: Integer);
    procedure Execute; override;
    property List: TStrings read fList;
  public
    constructor Create(Obj: TObject; DataLoaded: TDataLoadedProc; DataProgress: TDataProgressProc); reintroduce; virtual;
    destructor Destroy; override;
  end;

{ TLoadDataThread }

constructor TLoadDataThread.Create(Obj: TObject; DataLoaded: TDataLoadedProc; DataProgress: TDataProgressProc);
begin
  // Create thread not suspended
  inherited Create(False);
  // When finished, autofree
  FreeOnTerminate := True;
  // remember parameters
  fObj := Obj;
  fDataLoaded := DataLoaded;
  fDataProgress := DataProgress;
  fList := TStringList.Create;
end;

destructor TLoadDataThread.Destroy;
begin
  fList.Free;
  inherited Destroy;
end;

procedure TLoadDataThread.Execute;
const
  DataRows = 10;
  DataCols = 5;
var I, J: Integer;
  temp: string;
begin
  // Create the data
  List.Values['ROWS'] := IntToStr(DataRows +1);
  List.Values['COLS'] := IntToStr(DataCols +1);
  for I := 1 to DataRows +1 do
    for J := 1 to DataCols +1 do
    begin
      temp := '';
      if (J = 1) and (I > 1) then
        temp := Format('Row %d', [I-1])
      else if (I = 1) and (J > 1) then
        temp := Format('Column %d', [J-1])
      else if (J > 1) and (I > 1) then
        Temp := Format('Data %d_%d', [I-1, J-1]);
      List.Values[Format('%d_%d', [I, J])] := Temp;
      ReportProgress(Trunc(((I-1) * (DataCols+1) + J-1) / ((DataRows+1) * (DataCols+1))*100));
      Sleep(WaitSleep);
    end;
  ReportProgress(100);
  // Call the show procedure with the obj to show the created data in
  Synchronize(DoDataLoaded);
end;

procedure TLoadDataThread.ReportProgress(ProcentDone: Integer);
begin
  fProcentDone := ProcentDone;
  Synchronize(DoProgress);
end;

procedure TLoadDataThread.DoDataLoaded;
begin
  // Make sure we have all items assigned
  if Assigned(fDataLoaded) and Assigned(fObj) then
    fDataLoaded(fObj, fList);

  // clear the flag to stop a second thread
  mLoadDataThreadRunning := False;
end;

procedure TLoadDataThread.DoProgress;
begin
  if Assigned(fDataProgress) then
    fDataProgress(fObj, fProcentDone);
end;

function LoadData(obj: TObject; DataLoaded: TDataLoadedProc; DataProgress: TDataProgressProc): TLoadDataThread;
begin
  Result := nil;
  if not mLoadDataThreadRunning then
  begin
    mLoadDataThreadRunning := True;
    Result := TLoadDataThread.Create(Obj, DataLoaded, DataProgress);
  end;
end;

initialization
  mLoadDataThreadRunning := False;

end.

Open in new window

0
 
Sinisa VukCommented:
As I can see - you must create thread as suspended:
inherited Create(False);

Open in new window


Later - after you call function and it returns thread - do call Resume.
0
 
Geert GruwezOracle dbaCommented:
i see, you used 2 articles, the progress and the loaddata ... :)

 you've made a method function of an ordinary function
the loaddata is not part of the Thread class

the mainform uses the TThread ancestor class for resume and pause

the ordinary function will create a thread on the fly
> doesn't really matter if it's started immediately

the one thing still needed is cleanup
the thread should still call a finishing routine when finished to reset the variable for the thread to nil
or you'll get AV's here and there

hmmm, it's been a while since i worked with this code...
i'll try and create a sample with cleanup and multiple threads
0
 
Stef MerlijnDeveloperAuthor Commented:
That would be great. :-)
0
 
Geert GruwezOracle dbaCommented:
well ... here the sample is
what the threads do is rather trivial in this sample ... count to 100 and sleep a little

the simulation
> start 2 threads
> when they have both finished, start a 3rd thread
> when that 3rd thread is finished optionally start the first 2 again
and so on ...

ooo ... crap, no delphi dproj files allowed.
i'll add an attention for the mods :)

here is the zipped project:
prjEEThreads.zip
0
 
Geert GruwezOracle dbaCommented:
change the last line in the main unit to take the checkbox into account ... :)

  if Task3Done and chkRepeatSimul.Checked and (fThreadTask1 = nil) and  (fThreadTask2 = nil) then
    btnStartSimul.Click;
end;

end.

Open in new window

0
 
Stef MerlijnDeveloperAuthor Commented:
Hi Geert,

That is some great simulation, thank you...
0

Featured Post

Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

  • 5
  • 4
  • 2
  • +1
Tackle projects and never again get stuck behind a technical roadblock.
Join Now