Recent

Author Topic: [SOLVED] Using Write, Writeln in GUI app  (Read 7586 times)

tk

  • Sr. Member
  • ****
  • Posts: 364
[SOLVED] Using Write, Writeln in GUI app
« on: October 30, 2016, 03:58:18 pm »
I know there are tons of these topics everywhere but still I did not find the case which would exactly match my needs.

I have a console app that uses some Write(ln)s eg.:

Code: Pascal  [Select]
  1. Writeln('File ', FileName, ' has been saved.');

Now I want to use exactly that code in a GUI app, only redirect its output to a TMemo.
So when I step with debugger over this line the text appears in my TMemo.

Platform independent way in FPC/Laz. Is that possible?

Thank you

PS. Of course I know solutions without Write(ln), eg. via SysUtils.Format etc. but I am interested in this way, because of reusing the handy Write(ln) syntax.




« Last Edit: October 30, 2016, 10:09:10 pm by tk »

skalogryz

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 2280
    • havefunsoft.com
Re: Using Write, Writeln in GUI app
« Reply #1 on: October 30, 2016, 04:01:59 pm »
You'd need to override StdOut handle to either a file or even better to a pipe/memory.
And then you'll need some sort of routine, that would read from the file/pipe/memory the data entered.

Alternatively you might want to introduce you're own unit with Writeln() implementation(s) (lots of them!), but it fill not be the same as RTL Writeln.

---
Actually, if you want the line to appear in the memo, right after you step over the line in the debugger, overriding Writeln() in a separate unit is your only option.
---
Yet another way is to implement your own TextRec structure, and assign it as StdOut (output)
« Last Edit: October 30, 2016, 04:15:02 pm by skalogryz »
Patron Cocoa Widgetset development https://www.patreon.com/skalogryz

molly

  • Hero Member
  • *****
  • Posts: 2345
Re: Using Write, Writeln in GUI app
« Reply #2 on: October 30, 2016, 04:17:04 pm »
Actually, if you want the line to appear in the memo, right after you step over the line in the debugger, overriding Writeln() in a separate unit is your only option.
Why ? Purely interested as i don't understand the reasoning.

You can either write your won device driver (e.g. writeLn(mymemodevice, 'text');) or redirect standard out.

The 'problem' with a memo is implementing the write command as a memo works with adding lines. it can be done but is a bit awkward. Alternatively you could buffer the writes to only appear in the memo on a newline.

skalogryz

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 2280
    • havefunsoft.com
Re: Using Write, Writeln in GUI app
« Reply #3 on: October 30, 2016, 04:34:14 pm »
Actually, if you want the line to appear in the memo, right after you step over the line in the debugger, overriding Writeln() in a separate unit is your only option.
Why ? Purely interested as i don't understand the reasoning.

You can either write your won device driver (e.g. writeLn(mymemodevice, 'text') ; ) or redirect standard out.
It's easier, than writing your own device driver.
Which I actually added a bit later.
Patron Cocoa Widgetset development https://www.patreon.com/skalogryz

molly

  • Hero Member
  • *****
  • Posts: 2345
Re: Using Write, Writeln in GUI app
« Reply #4 on: October 30, 2016, 04:38:07 pm »
@skalogryz
Ok, redirecting and writing own driver is not the easiest of tasks so it would indeed be the easier solution.

Sorry, as i did not wanted to cross your answer.

skalogryz

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 2280
    • havefunsoft.com
Re: Using Write, Writeln in GUI app
« Reply #5 on: October 30, 2016, 04:39:29 pm »
Sorry, as i did not wanted to cross your answer.
No need to be sorry, that's the purpose of the forum - to cross answers, share opinions, code and experience.

Writing your own driver, could be an ultimate solution (that others could use as well), so it might worth to invest a bit more time into it.
Patron Cocoa Widgetset development https://www.patreon.com/skalogryz

howardpc

  • Hero Member
  • *****
  • Posts: 3177
Re: Using Write, Writeln in GUI app
« Reply #6 on: October 30, 2016, 09:48:00 pm »
Attached project is a proof of concept of a textfile device driver using a memo.

tk

  • Sr. Member
  • ****
  • Posts: 364
Re: Using Write, Writeln in GUI app
« Reply #7 on: October 30, 2016, 10:06:12 pm »
Excellent Howard, exactly what I wanted!

Btw. your test adds one empty line into the Memo (because the f.bufptr already contains CR+LF and memo inserts another pair).
This fixes it:

Code: Pascal  [Select]
  1. function MemoOutput(var f: TTextRec): integer;
  2. var
  3.   succeeded: boolean;
  4.   S: string;
  5.   L: Integer;
  6. begin
  7.   if (f.bufpos <> 0) then begin
  8.     f.buffer[f.bufpos]:=#0;
  9.     repeat
  10.       succeeded:=True;
  11.       try
  12.         S := PChar(f.bufptr);
  13.         L := Pos(#13, S);
  14.         if L > 0 then
  15.           S[L] := #0;
  16.         WriteLnMemoForm.WritelnMemo.Lines.Add('[WritelnMemo] ' + S);
  17.       except
  18.         succeeded:=False;
  19.       end;
  20.     until succeeded;
  21.     f.bufpos:=0;
  22.   end;
  23.   Result:=0;
  24. end;
  25.  

Very good work, thank you!

PS. Didn't test with Write as actually I need only Writeln now.

molly

  • Hero Member
  • *****
  • Posts: 2345
Re: [SOLVED] Using Write, Writeln in GUI app
« Reply #8 on: October 30, 2016, 10:53:29 pm »
thank you howardpc.

Poor person's redirect solution
Code: Pascal  [Select]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls;
  9.  
  10. type
  11.  
  12.   { TMemoStream }
  13.  
  14.   TMemoStream = class(TStringStream)
  15.   strict private
  16.     FMemo: TMemo;
  17.   public
  18.     constructor Create(const AMemo: TMemo); virtual; reintroduce;
  19.     function Write(const Buffer; Count: longint): longint; override;
  20.   end;
  21.  
  22.   { TForm1 }
  23.  
  24.   TForm1 = class(TForm)
  25.     Button1: TButton;
  26.     Memo1: TMemo;
  27.     procedure Button1Click(Sender: TObject);
  28.     procedure FormCreate(Sender: TObject);
  29.     procedure FormDestroy(Sender: TObject);
  30.   private
  31.     { private declarations }
  32.   public
  33.     { public declarations }
  34.   end;
  35.  
  36. var
  37.   Form1: TForm1;
  38.   MemoStream : TMemoStream;
  39.   OldOutPut  : Text;
  40.  
  41. implementation
  42.  
  43. {$R *.lfm}
  44.  
  45. uses
  46.   StreamIO;
  47.  
  48. { TMemoStream }
  49.  
  50. constructor TMemoStream.Create(const AMemo: TMemo);
  51. begin
  52.   inherited Create(EmptyStr);
  53.   Assert(Assigned(AMemo));
  54.   FMemo := AMemo;
  55. end;
  56.  
  57. function TMemoStream.Write(const Buffer; Count: longint): longint;
  58. begin
  59.   Result:=inherited Write(Buffer, Count);
  60.   FMemo.Lines.Text := DataString;
  61. end;
  62.  
  63. { TForm1 }
  64.  
  65. procedure TForm1.FormCreate(Sender: TObject);
  66. begin
  67.   OldOutPut := OutPut;
  68.  
  69.   MemoStream := TMemoStream.Create(Memo1);
  70.   AssignStream(Output, MemoStream);
  71.   Rewrite(Output);
  72. end;
  73.  
  74. procedure TForm1.FormDestroy(Sender: TObject);
  75. begin
  76.   MemoStream.Free;
  77.   OutPut := OldOutPut;
  78. end;
  79.  
  80. procedure TForm1.Button1Click(Sender: TObject);
  81. begin
  82.   Write('hello');
  83.   WriteLn(' world');
  84. end;
  85.  
  86. end.
  87.  

Leledumbo

  • Hero Member
  • *****
  • Posts: 8111
  • Programming + Glam Metal + Tae Kwon Do = Me
Re: [SOLVED] Using Write, Writeln in GUI app
« Reply #9 on: October 31, 2016, 02:10:43 am »
Poor person's redirect solution
I prefer this solution to any other handcrafted one, that's the purpose of StreamIO unit.

tk

  • Sr. Member
  • ****
  • Posts: 364
Re: [SOLVED] Using Write, Writeln in GUI app
« Reply #10 on: October 31, 2016, 09:00:33 am »
Poor person's redirect solution

Thank you molly.
This solution does not do exactly the same, ie. what I needed, but it can be useful for others.
Btw. I tried similar thing via the StreamIO but forgot to call Rewrite and got some IO error.

EDIT: Actually this solution can be persuaded to do the same, ie. not to rewrite entire memo content after each call:

Code: Pascal  [Select]
  1. function TMemoStream.Write(const Buffer; Count: longint): longint;
  2. var
  3.   S: string;
  4.   L: Integer;
  5. begin
  6.  Result:=inherited Write(Buffer, Count);
  7. // FMemo.Lines.Text := DataString;
  8.   SetString(S, PAnsiChar(@Buffer), Count);
  9.   L := Pos(#13, S);
  10.   if L > 0 then
  11.     S[L] := #0;
  12.   WriteLnMemoForm.WritelnMemo.Lines.Add('[WritelnMemo] ' + S);
  13. end;
  14.  
So both solutions are usable.
« Last Edit: October 31, 2016, 09:19:34 am by tk »

Thaddy

  • Hero Member
  • *****
  • Posts: 9167
Re: [SOLVED] Using Write, Writeln in GUI app
« Reply #11 on: October 31, 2016, 10:01:32 am »
Sigh, boring, silly, but: Use a macro:
Code: Pascal  [Select]
  1. unit Unit1;
  2.  
  3. {$mode delphi}
  4. {$macro on}
  5. {$define writeln := Memo1.lines.add}
  6. interface
  7.  
  8. uses
  9.   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls;
  10.  
  11. { TForm1 }
  12.  
  13. type
  14.   TForm1 = class(TForm)
  15.     Button1: TButton;
  16.     Memo1: TMemo;
  17.     procedure Button1Click(Sender: TObject);
  18.   private
  19.     { private declarations }
  20.   public
  21.     { public declarations }
  22.   end;
  23.  
  24. var
  25.   Form1: TForm1;
  26.  
  27. implementation
  28.  
  29. {$R *.lfm}
  30.  
  31. { TForm1 }
  32.  
  33. procedure TForm1.Button1Click(Sender: TObject);
  34. begin
  35.   Writeln('Test');  // macro kicks in...
  36. end;
  37.  
  38. end.
       
Get the point?  Works! ;) Recommended? depends!  8-)
also related to equus asinus.

rvk

  • Hero Member
  • *****
  • Posts: 3842
Re: [SOLVED] Using Write, Writeln in GUI app
« Reply #12 on: October 31, 2016, 10:18:54 am »
Sigh, boring, silly, but: Use a macro:
...
Get the point?
Thaddy, you are missing the point.
In your case you could also just add a function writeln(S:String);

But the point is that writeln() takes multiple parameters with multiple types.
You can do writeln('string', 21, 5:4); etc.

Can you do that with the same macro-method ?
« Last Edit: October 31, 2016, 10:23:59 am by rvk »

Thaddy

  • Hero Member
  • *****
  • Posts: 9167
Re: [SOLVED] Using Write, Writeln in GUI app
« Reply #13 on: October 31, 2016, 10:51:07 am »
That's why I added the depends....
You could also redefine the macro using writestr..... I guess ;) were it not that our macro facility is rather crude. So it needs a manually written function to make you happy :).

But IO redirection is the way to go as I already explained in 2001 (TwritelnMemo) . But Peter Below gave a similar example at that point in time that was actually better.
edit: I add his code
Code: Pascal  [Select]
  1. {== Unit StreamIO =====================================================}
  2. {: Implements a text-file device driver that allows textfile-style I/O
  3.    on streams.
  4. @author Dr. Peter Below
  5. @desc   Version 1.0 created 4 Januar 2001<BR>
  6.         Current revision 1.0<BR>
  7.         Last modified       4 Januar 2001<P>                           }
  8. {======================================================================}
  9. Unit StreamIO;
  10.  
  11. Interface
  12. Uses classes;
  13.  
  14. {-- AssignStream ------------------------------------------------------}
  15. {: Attach a stream to a Textfile to allow I/O via WriteLn/ReadLn
  16. @Param F is the textfile to attach the stream to
  17. @Param S is the stream
  18. @Precondition  S <> nil
  19. @Desc The passed streams position will be set to 0 by Reset and Rewrite
  20.   and to the streams end by Append. The stream is not freed when the
  21.   textfile is closed via CloseFile and it has to stay in existence as
  22.   long as the textfile is open.
  23. }{ Created 4.1.2001 by P. Below
  24. -----------------------------------------------------------------------}
  25. Procedure AssignStream( Var F: Textfile; S: TStream );
  26.  
  27. Implementation
  28.  
  29. Uses sysutils;
  30.  
  31. {-- GetDevStream ------------------------------------------------------}
  32. {: Get the stream reference stored in the textrec userdata area
  33. @Param F is the textfile record
  34. @Returns the stream reference
  35. @Postcondition result <> nil
  36. }{ Created 4.1.2001 by P. Below
  37. -----------------------------------------------------------------------}
  38. Function GetDevStream( Var F: TTextRec ): TStream;
  39.   Begin { GetDevStream }
  40.     Move( F.Userdata, Result, Sizeof( Result ));
  41.     Assert( Assigned( Result ));
  42.   End; { GetDevStream }
  43.  
  44. {-- DevIn -------------------------------------------------------------}
  45. {: Called by Read, ReadLn etc. to fill the textfiles buffer from the
  46.    stream.
  47. @Param F is the textfile to operate on
  48. @Returns 0 (no error)
  49. }{ Created 4.1.2001 by P. Below
  50. -----------------------------------------------------------------------}
  51. Function DevIn( Var F: TTextRec ): Integer;
  52.   Begin { DevIn }
  53.     Result := 0;
  54.     With F Do Begin
  55.       BufEnd := GetDevStream(F).Read( BufPtr^, BufSize );
  56.       BufPos := 0;
  57.     End; { With }
  58.   End; { DevIn }
  59.  
  60. {-- DevFlushIn --------------------------------------------------------}
  61. {: A dummy method, flush on input does nothing.
  62. @Param F is the textfile to operate on
  63. @Returns 0 (no error)
  64. }{ Created 4.1.2001 by P. Below
  65. -----------------------------------------------------------------------}
  66. Function DevFlushIn( Var F: TTextRec ): Integer;
  67.   Begin { DevFlushIn }
  68.     Result := 0;
  69.   End; { DevFlushIn }
  70.  
  71. {-- DevOut ------------------------------------------------------------}
  72. {: Write the textfile buffers content to the stream. Called by Write,
  73.    WriteLn when the buffer becomes full. Also called by Flush.
  74. @Param F is the textfile to operate on
  75. @Returns 0 (no error)
  76. @Raises EStreamError if the write failed for some reason.
  77. }{ Created 4.1.2001 by P. Below
  78. -----------------------------------------------------------------------}
  79. Function DevOut( Var F: TTextRec ): Integer;
  80.   Begin { DevOut }
  81.     Result := 0;
  82.     With F Do
  83.       If BufPos > 0 Then Begin
  84.         GetDevStream(F).WriteBuffer( BufPtr^, BufPos );
  85.         BufPos := 0;
  86.       End; { If }
  87.   End; { DevOut }
  88.  
  89. {-- DevClose ----------------------------------------------------------}
  90. {: Called by Closefile. Does nothing here.
  91. @Param F is the textfile to operate on
  92. @Returns 0 (no error)
  93. }{ Created 4.1.2001 by P. Below
  94. -----------------------------------------------------------------------}
  95. Function DevClose( Var F: TTextRec ): Integer;
  96.   Begin { DevClose }
  97.     Result := 0;
  98.   End; { DevClose }
  99.  
  100. {-- DevOpen -----------------------------------------------------------}
  101. {: Called by Reset, Rewrite, or Append to prepare the textfile for I/O
  102. @Param F is the textfile to operate on
  103. @Returns 0 (no error)
  104. }{ Created 4.1.2001 by P. Below
  105. -----------------------------------------------------------------------}
  106. Function DevOpen( Var F: TTextRec ): Integer;
  107.   Begin { DevOpen }
  108.     Result := 0;
  109.     With F Do Begin
  110.       Case Mode Of
  111.         fmInput: Begin { Reset }
  112.             InOutFunc := @DevIn;
  113.             FlushFunc := @DevFlushIn;
  114.             BufPos := 0;
  115.             BufEnd := 0;
  116.             GetDevStream( F ).Position := 0;
  117.           End; { Case fmInput }
  118.         fmOutput: Begin { Rewrite }
  119.             InOutFunc := @DevOut;
  120.             FlushFunc := @DevOut;
  121.             BufPos := 0;
  122.             BufEnd := 0;
  123.             GetDevStream( F ).Position := 0;
  124.           End; { Case fmOutput }
  125.         fmInOut: Begin { Append }
  126.             Mode := fmOutput;
  127.             DevOpen( F );
  128.             GetDevStream(F).Seek( 0, soFromEnd );
  129.           End; { Case fmInOut }
  130.       End; { Case }
  131.     End; { With }
  132.   End; { DevOpen }
  133.  
  134. Procedure AssignStream( Var F: Textfile; S: TStream );
  135.   Begin { AssignStream }
  136.     Assert( Assigned( S ));
  137.     With TTextRec(F) Do Begin
  138.       Mode := fmClosed;
  139.       BufSize := SizeOf(Buffer);
  140.       BufPtr := @Buffer;
  141.       OpenFunc := @DevOpen;
  142.       CloseFunc := @DevClose;
  143.       Name[0] := #0;
  144.       { Store stream reference into Userdata area }
  145.       Move( S, Userdata, Sizeof(S));
  146.     End; { With }
  147.   End; { AssignStream }
  148.  
  149. End { StreamIO }.
  150.  
  151.  
  152. Peter Below (TeamB)  10011...@compuserve.com)
  153. No e-mail responses, please, unless explicitly requested!
  154. Note: Till Feb.2001 i will only visit the groups on weekends, so  be patient
  155. if i don't reply immediately.


Bit of history,that... ;)
« Last Edit: October 31, 2016, 12:23:50 pm by Thaddy »
also related to equus asinus.

Thaddy

  • Hero Member
  • *****
  • Posts: 9167
Re: [SOLVED] Using Write, Writeln in GUI app
« Reply #14 on: October 31, 2016, 12:30:08 pm »
Also note that one was for Delphi. The record structure is a bit different in FPC but easy to translate.
[edit]
and Rik, the OP could probably use the macro. Combined with writestr.
« Last Edit: October 31, 2016, 01:03:36 pm by Thaddy »
also related to equus asinus.