Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations strongm on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Write to a Memo from many different Units

Status
Not open for further replies.

6volt

Programmer
Jun 4, 2003
74
US
I have a number of non-GUI style programs (engineering programs for research) where I want to maintain a "console memo," a memo used by all Procedures in all Units to log various information such as intermediate results, progress, etc.

I was thinking of a U_Console unit which would have the form with the Console Memo and a U_MyCommon unit with the procedures that would be used to write to that memo from other units along with other procedures I would normally use with each program I write.

However, there seem to be issues regarding what is in my "top" unit and what unit/procedure my "main program" is located.

I need to in Unit_X, run a Proc from U_MyCommon to write to a Memo1 in U_Console.

There are also issues with USES with hierarchical units. (For one if A Uses B, and B Uses C, I find that C is not available to A and that A Uses C is required to run stuff in C. One would think that Since B Uses C, and A Uses B, then C would be available to A but it appears not. Do I have that right?)

I would get into the details of Types and Vars and Interface and Implementation, to cover what I have done, but since I don't have any success (or an intrinsic understanding at this level), I believe that would simply confuse this post.

I think what I want to do is simple in concept and should be simple to implement, however, I have failed to figure out how to do this.

Any help would be greatly appreciated since I have spent a number of days trying different approaches unsuccessfully. I have also been unable to find anything on the internet about this particular topic to make matters worse.

Thanks in advance,
Tom
 
I'm not sure I have a complete understanding of what you're trying to do or what you are asking.

One would think that Since B Uses C, and A Uses B, then C would be available to A but it appears not. Do I have that right?

A unit has to be explicitly defined in another for it to be accessible. The contents of the interface section of C is accessible in B, the interface section of B in A.

I would get into the details of Types and Vars and Interface and Implementation, to cover what I have done,

It would be good to get into the interface level and the unit level, at least so we can understand more of what you are looking to do, if the explanation below doesn't help.

Explanation from an app I've done: Let's say you have an app that, by necessity in design/testing, can be split up into 4 units (A, B, C, and D). Each unit has a long set of functions in source that work on a specific data record in some fashion. As you can guess, the record is complex, so you have multiple type definitions which are equally applicable to all 4 units. Moreover, you have some other definitions which are applicable to 2, or 3 of the units as well, depending on the function. So what do you do?

There are two ways to approach it: You can define all the units in each of the other units, or you can make the definitions in each of the units (not to mention in any program that would use it). There you have the problem there of the typing, as well as portability. Myrecord would be different in A and B, even if you define them the same way with the same words. Or the best way is to create fifth unit, E, which has all relevant definitions necessary for all the units and then you include that unit in the other four. Then I have the bonus of being able to define unit E in my main program, along with a main function which handles the calls to the other 4 units successfully.

I don't know if that answers your question or not. But since your post is not clear on your specific issue, it may be one of design discreteness (keep all related code together), or a number of other things.

I have also been unable to find anything on the internet about this particular topic to make matters worse.

This is no surprise, few seem to have developed multiple unit applications from what I have read (no need perhaps), and even fewer have written about it.

----------
Measurement is not management.
 
Thanks for the reply Glenn,

I sort of mixed 2 different problems I'm having.

The first is the USES statement.

Here is the problem (all uses in the Interface):

B uses C, then A uses B

I would think that B uses C would have brought everything from C into B, however, that appears to NOT be the case when A uses B. For A to execute anything in C, the following is required:

A uses B,C and B uses C which is counter intuitive.

It is frustrating that there is no talk of cascading uses.

___________________________________________

The second problem is specifically the memo.add statement when the memo you want to add to is in another Unit.

For discussion U_... denotes a unit.

Given

U_main: 1) contains the memo, Memo1, 2) uses U_Common, 3) calls PROC WriteToMemo to write to Memo1

U_common: 1) contains the PROCEDURE WriteToMemo

So U_Common cannot use U_Main where Memo1 is, so how do you make WriteToMemo write to Memo1?

There is a way to use Sender, but it requires other structure to work. (I got Sender to work in one program, but failed for 2 days to get it to work in another program, so I'm not convinced Sender is the best way to do it.)

If worst comes to worst, I would strip down the 2 programs I have, one which seems to work and the other which will not work for people to look at.

Thanks
Tom
 
this is a classic one.

you say you have troubles with unit dependencies, a simple solution: don't do it, abstract the code between your units.

I write a lot of non visual programs (mainly services).

So I too needed a way to log my code into a file and a memo when I'm debugging.

I created a threaded debugger that I reuse in all my programs, but it is too complicated for what I want to explain now.

all need is to create a sort of logger unit , let's say u_log:

Code:
unit u_log

interface

uses Classes, SysUtils;

type
  TLogEvent = procedure(Str : string) of object;

  Logger = class
  private
    FLogEvent : TLogEvent;
  public  
    procedure write(str : string);
    property OnLogEvent: TLogEvent read FLogEvent write FLogEvent; 
  end;

var Logger : TLogger;

implementation

procedure TLogger.Write(Str : string);
begin
 if Assigned(FLogEvent) then
 FLogEvent(Str);
end;

initialization
 Logger := TLogger.Create;

finalization
 FreeAndNil(Logger);
end.

the code above will create a TLogger object for you.
so all you have to do is include u_log in all units where you want to log.

logging output is as simple as:

Logger.Write('logging output');

the unit that contains the TMemo needs some extra code :
I assume your form is called Frm_log in this example

Code:
unit u_memo;

interface

uses u_log,...

type 
  TFrm_log = class(TForm)
  private
   ...
    procedure LogEvent(Str : string);
    procedure FormCreate(Sender : TObject);
  public
   ...
  end;
implementation

...

procedure TFrm_log.LogEvent(Str : string);
begin
 // here we will output the string to our memo
 Memo1.Lines.Add(Str); 
end;

procedure TFrm_log.FormCreate(Sender : TObject);
begin
 // OnFormCreate EventHandler, is called when the form is created
 Logger.OnLogEvent := LogEvent;

end;

end.

I hope this makes sense to you. I did not test this code, be aware of eventual typos.

Cheers,
Daddy

-----------------------------------------------------
What You See Is What You Get
Never underestimate tha powah of tha google!
 
@6volt

A1) It is never appropriate to dump foreign namespaces into the current unless the current explicitly asks for it. Delphi units are simplistic, but they do the right thing.

As a visual thought experiment, consider your car. Your car needs an engine to run, but you don't necessarily want to see the engine every time you use your car. If you really do want to operate the throttle with your fingers, you'll have to take off the hood and windshield so that you can have direct access to the engine as well as the car.

A2) Circular unit dependencies are a chicken-and-egg sort of thing --something, BTW, that Pascal is explicitly designed to disallow.

The interface section of a unit says, "this is what I need to provide this interface (uses clause) and this interface provides these objects (everything else: types, consts, vars, procedures, etc).

If the unit needs more stuff to actually do the job, they should be listed in the implementation section's uses clause. The implementation section says, "this is what I need to do the job and this is how I do it."

So, two separate units can depend on each other, but at least one their interfaces must be independent of the other.

Finally, the main program is a special unit: it uses (or depends on) other things, but nothing can depend on (or use) it. This is natural; the master cannot be the servant's servant.

Hope this helps.
 
Thanks so much for the suggestions especially Whosrdaddy for the code. (BTW, I play to try it probably on Monday. I will incorporate it by making each of your codes into units, then use the appropriate unit in my code units.

I just thought of something else for a "non-GUI" style program. I've been told you have to have start with a form even if your code doesn't need a GUI.

I have 2 approaches when using this method:
1) I have my main unit open a form and then place the main program code in the form OnCreate Procedure.
2) Same as one except that main program is in a U_MAINPROGRAM unit which is USED and then called in the OnCreate Procedure. (This results in the Form Unit being the Top Unit in the Uses Tree. In particular, it is ABOVE the Main Program Unit.)

There have been some issues indicating that OnCreate is not a good choice since really want to proceed after the form is completely opened, so have also used OnShow. Not sure of this nuance.

I have successfully implemented the log memo in type 2 above using Sender.memo1.add (I'll post a block of code illustrating this method.) However, I don't understand why I can't get it to work in type 1 above. (This only means I don't know what I am doing, unfortunately.)

Thanks again all!
Tom
 
I just thought of something else for a "non-GUI" style program. I've been told you have to have start with a form even if your code doesn't need a GUI.

Not true. You can start with a DPR file and make a console program very easily. The editor typically doesn't make it easy, however. I have to go create the new text file with the DPR extension and then pull it up in Delphi, do some edits and save before I begin.

But once I do that, I have no problems. And your code size will go down tremendously, too.

----------
Measurement is not management.
 
whosrdaddy,

Am working on implementing your code.
I think I found a small errata in U_Log:

type
...
Logger = class

should be:

type
...
TLogger = class:

Thanks
Tom
 
argh....

No colon after class

(how did that get there...)

and we have no EDIT which kind of sucks
 
lol, doesn't matter.

[thumbsup]

-----------------------------------------------------
What You See Is What You Get
Never underestimate tha powah of tha google!
 
whosrdaddy, thanks for the code.

In the process of studying/implementing it, I was also trying to determine why mine worked in one project and not in another.

My code was based on the Sender approach:

Code:
PROCEDURE LOGmemo ( msg:String; VAR Sender : TMemo );
BEGIN
  Sender.lines.add (msg);
  application.ProcessMessages:
END;

where the Sender parameter would be something like
Code:
  FormMain.Memo1
where FormMain was the Form name, and Memo1 was the memo name.

What was happening was in the project where the main program code was placed in the startup dummy form PROCEDURE ONxxx, I was using OnCreate which lead to weirdness since the dummy form was not completed when the main code execution began.

Merely changing it to OnShow solved the problem. This was also the event I used on the project that did work.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top