Improve company productivity with a Business Account.Sign Up

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

3 level software architecture

what is the best way to move progress information from a 3rd. level routine to the GU level ???

procedure button1.click (......)
begin
         
         MyClass.DoSomeAction (...;  aGuiProgressBar; ......);

end;


procedure TMyClass.DoSomeAction (........aGuiProgressBar : TProgressbar...);
begin
   
          ExecuteComplexCalc( ...aGuiProgressBar .... ...);
end;



procedure  ExecuteComplexCalc (........ .aGuiProgressBar : TProgressbar   .....................  )
begin
         

            aGuiProgressBar.value := i;       //  set the value here

end;
0
BdLm
Asked:
BdLm
  • 6
  • 3
  • 2
  • +1
7 Solutions
 
Geert GOracle dbaCommented:
1 way: read my article on this site
probably not the best, but it shows independance of a class towards a form using a callback procedure

a lot of solutions i see, have the name of the form or the progress inside the thread
which is useless if using the same unit in a other unit/form with a different name
0
 
BdLmAuthor Commented:
you refer to http://www.experts-exchange.com/Programming/Languages/Pascal/Delphi/A_239-Displaying-progress-in-the-main-form-from-a-thread-in-Delphi.html  ??  

from design style level 3 should only talk to level 2 but never to level 1, but this is much more coding instead of the dirty way .....

I need the reuse design as I use the level 3 libraries for many different projects
0
 
epasquierCommented:
I'm not sure what your problem is. Your way could work, with just the addition of something to tell the progressbar to update itself once set to a new value. Otherwise, passing the reference to the progressbar from one level to the next is not a problem.

In fact, you can use a SetProgress global function that will do that, and other things very much needed regularly during lengthy calculations, like treating pending windows events
For a Cancel button for example, that would break the current calculation.
procedure SetProgress(aGuiProgressBar : TProgressbar; Val:Integer);
begin
 aGuiProgressBar.value := i;       //  set the value here
 Application.ProcessMessages; // treat all pending system messages NOW
// among them, redraw the progress bar, and a cancel button event
 if Canceled Then Abort; // raise a silent exception that will break all the process
end;

procedure TForm.btnCancelClick(Sender:TObject);
begin
 Canceled:=True; // Canceled is a public variable set to false at start
 // this will abort the process next time its progress is supdated
end;

procedure  ExecuteComplexCalc (........ .aGuiProgressBar : TProgressbar   .....................  );
Var
 i:integer;
begin
 for i:=0 to 1000 do 
  begin
   SetProgress(aGuiProgressBar , i);
   sleep(1000);
  end;
end;


procedure TMyClass.DoSomeAction (........aGuiProgressBar : TProgressbar...);
begin
 Canceled:=False;
 ExecuteComplexCalc( ...aGuiProgressBar .... ...);
end;

Open in new window

0
Get expert help—faster!

Need expert help—fast? Use the Help Bell for personalized assistance getting answers to your important questions.

 
Ephraim WangoyaCommented:
Always try as much as possible to separate your GUI controls from your backend controls. This way if you need to change your progress bar at some time in the future, you only do so in the interface part of your program

Heres what I would do

  TUpdateProgressEvent = procedure(Sender: TObject; const AProgress: Int64);

  TMyObject = class(TSomething)
  private
    FOnUpdateProgressEvent: TUpdateProgressEvent;
    procedure DoProgressEvent(const AProgress: Int64);
  public
    procedure DoSomeAction;
    property OnUpdateProgressEvent: TUpdateProgressEvent read FOnUpdateProgressEvent write FOnUpdateProgressEvent;
  end;

  TForm1 = class(TForm)
  private
    procedure OnUpdateProgress(Sender: TObject; const AProgress: Int64);
  public
    procedure DoSomething;
  end;

......

{ TMyObject }

procedure TMyObject.DoProgressEvent(const AProgress: Int64);
begin
  if Assigned(FOnUpdateProgressEvent) then
    FOnUpdateProgressEvent(Self, AProgress);
end;

procedure TMyObject.DoSomeAction;
var
  I: Integer;
begin
  for I := 0 to 1000 do
    DoProgressEvent(I);
end;

{ TForm1 }

procedure TForm1.DoSomething;
var
  MyObject: TMyObject;
begin
  MyObject := TMyObject.Create;
  try
    //initialize your progress bar
    MyObject.OnUpdateProgressEvent := OnUpdateProgress;
    MyObject.DoSomeAction;
  finally
    FreeAndNil(MyObject);
  end;
end;

procedure TForm1.OnUpdateProgress(Sender: TObject; const AProgress: Int64);
begin
  ProgressBar1.Value := AProgress;
  //Application.ProcessMessages;
end;
0
 
Geert GOracle dbaCommented:
um, ewangoya,
that's what that article is about ...

and the reason i wrote it, was to reference it
try reading it ...

you're almost there, you still need to let the progressbar update it's on screen display
ProgressBar.Value := aProgress;
ProgressBar.Update;

ow ... the TSomething ... descending from TThread ?
0
 
Geert GOracle dbaCommented:
I agree with the separation,
but in this case consider the observer pattern

even for multiple levels

i'll show a sample in a minute with different  levels and the observer pattern
0
 
Geert GOracle dbaCommented:
well i was going to create a sample, but then i bumped into this
http://tdelphihobbyist.blogspot.com/2009/11/observer-design-pattern-in-delphi-pull.html

if you think about levels in coding then you should certainly look at design patterns
you might want to think about mvc (model-view-controller) ...
0
 
Ephraim WangoyaCommented:
@Geert_Gruwez

I should have read your article first. Same thing only you pass the procedure pointer on create.
0
 
Geert GOracle dbaCommented:
yes, passing parameters gives total indepedence to the lower level
0
 
BdLmAuthor Commented:
what about for level 3 :

function heavyCalcLevel3(.......;  aFeedBackElement : TObject);
begin


        if  ( aFeedBackElement  is TProgresslbar then  
                    begin

                      ......

                   end;


end;

Thanks for your discussion
0
 
Geert GOracle dbaCommented:
you are expecting inside the thread that you are working with a progressbar
this limits the GUI to giving a progressbar to your thread

what if someone invented this really cool amazing TCalcStatus object which had a PercentDone value as a property

if you want to take a general approach to a problem then don't pin it down on using a TProgressbar

in nearly all calculations which have a indication with feedback
it's up to the gui how to display the progress or percentdone, not the thread
the thread is there to do the hard work, not the fancy screen work

a sample: lets consider something like bit torrent > this can download several files with different threads

level 1: download x number of files
level 2: download 1 specific file out of this list
level 3: download a chunk of this specific file

so:
in level 3: you have x number of bytes out of a total of Y >> translate to x/y*100 for percent done of this thread
in level 2: you could have 10 threads running, each downloading a chunk
  > the total of downloaded bytes / total file size * 100 for percent done of this file
in level 1: you could show the percentage of total bytes downloaded / total files size > total percent done

this is allways passing x number out of y number to do
i didn't talk about a progressbars yet in these threads

and there is the problem ... you could have a progressbar for each individual percentage
so you would need to report the individual progress to the higher level using x,y
it's allways up to the higher level what it does with this value ...

and how display it ?
i would do this within a devexpress TcxProgressbar within a devexpress TcxGrid
Off course it would have a progressbar for each level


so i would use this as feedback

type 
  TFeedbackProgressEvent = 
    procedure (Sender: TObject; WorkDone, WorkToDo: integer) of object;

function heavyCalcLevel3(.......;  FeedBack : TFeedbackProgressEvent);
var x, y: integer;
begin
  x := 0;
  y := FileSize('x.zip');
  Feedback(Self, x, y);
  repeat
    file.ReadByte;
    x := x +1;
    Feedback(Self, x, y);
  until x >= y;
  Feedback(Self, y, y);
end;

type
  THeavyCalc2 = class
  private
    fFilesToProcess: TList;
    fTotalFilesSize: Cardinal;
    fTotalWorkDone: Cardinal;
    Calc3Threads: TList;
    procedure FeebackEvent(Sender: TObject; WorkDone, WorkToDo: integer);  
    
  end;

procedure THeavyCalc2.FeebackEvent(Sender: TObject; WorkDone, WorkToDo: integer);
begin
  if WorkDone = WorkToDo then 
  begin
    fTotalWorkDone := fTotalWorkDone + WorkToDo;
    Feeback(Self, fTotalWorkDone,  fTotalFileSize);
  end; 
end;

Open in new window

0
 
epasquierCommented:
BdLm, Thanks for giving me some points, Geert has done a great job helping you with this topic where I only added stone to the building. I was ready to accept I would get no credit.

BUT :
When you select multiple answers, please do not select everything without consideration of the value of the answers. And you can change the amount of points each answers is worth, not just let the automatic division calculate it for you. With your way, it gives the impression that it is only needed to post a maximum of comments whatever the quality, and if more than one is selected then you will have better reward than a single complete explanation.

for example, you selected ewangoya #33460548 and geert response about it. They bring no value to the discussion.

Next time, select only the real answers to the question and change the value of the ones that helped you most (geert last post in this case is the main one and is worth by itself 300-400 pts). This will also ensure that the 'ACCEPTED SOLUTION' is the one with most points, so the one that really deserves it.
0
 
BdLmAuthor Commented:
Thanks for the inputs, my intension was to honor the complete discusssion, even geert again made a great job with very useful inputs for me. Should increase to points to 1000 :-)  

btw:

is there any language forcing people to develop software in 3 levels of architecture ? getting contributions to my software project from different people I get crazy if the violate the 3 level design style ....  
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

Join & Write a Comment

Featured Post

Free Tool: Path Explorer

An intuitive utility to help find the CSS path to UI elements on a webpage. These paths are used frequently in a variety of front-end development and QA automation tasks.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

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