Solved

How to force listening to buttonclick

Posted on 2014-02-24
12
270 Views
Last Modified: 2014-02-25
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
Comment
Question by:Delphiwizard
  • 5
  • 4
  • 2
  • +1
12 Comments
 
LVL 24

Expert Comment

by:jimyX
ID: 39882360
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
 

Author Comment

by:Delphiwizard
ID: 39882605
I'll try to use it and see what happens.
0
 
LVL 25

Assisted Solution

by:Sinisa Vuk
Sinisa Vuk earned 100 total points
ID: 39882972
you can use threaded job to accomplish this. (Like BMThread). Thread will not stop your app and it can be easily stopped/paused.
0
 
LVL 36

Accepted Solution

by:
Geert Gruwez earned 400 total points
ID: 39884862
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
 

Author Closing Comment

by:Delphiwizard
ID: 39885000
Thank you all for the support.
I'll went for the approach suggested by Geert Gruwez. The posted article is great Geert.
0
 

Author Comment

by:Delphiwizard
ID: 39885141
@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
Do You Know the 4 Main Threat Actor Types?

Do you know the main threat actor types? Most attackers fall into one of four categories, each with their own favored tactics, techniques, and procedures.

 
LVL 25

Expert Comment

by:Sinisa Vuk
ID: 39885157
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
 
LVL 36

Expert Comment

by:Geert Gruwez
ID: 39885173
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
 

Author Comment

by:Delphiwizard
ID: 39885236
That would be great. :-)
0
 
LVL 36

Expert Comment

by:Geert Gruwez
ID: 39885288
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
 
LVL 36

Expert Comment

by:Geert Gruwez
ID: 39885308
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
 

Author Comment

by:Delphiwizard
ID: 39886187
Hi Geert,

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

Featured Post

Highfive Gives IT Their Time Back

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

The uses clause is one of those things that just tends to grow and grow. Most of the time this is in the main form, as it's from this form that all others are called. If you have a big application (including many forms), the uses clause in the in…
Introduction The parallel port is a very commonly known port, it was widely used to connect a printer to the PC, if you look at the back of your computer, for those who don't have newer computers, there will be a port with 25 pins and a small print…
In this seventh video of the Xpdf series, we discuss and demonstrate the PDFfonts utility, which lists all the fonts used in a PDF file. It does this via a command line interface, making it suitable for use in programs, scripts, batch files — any pl…
Illustrator's Shape Builder tool will let you combine shapes visually and interactively. This video shows the Mac version, but the tool works the same way in Windows. To follow along with this video, you can draw your own shapes or download the file…

707 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

18 Experts available now in Live!

Get 1:1 Help Now