Zugriff auf TMemo von einem nichtgrafischen Objekt

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Antworten
charlytango
Beiträge: 843
Registriert: Sa 12. Sep 2015, 12:10
OS, Lazarus, FPC: Laz stable (2.2.6, 3.x)
CPU-Target: Win 32/64, Linux64
Wohnort: Wien

Zugriff auf TMemo von einem nichtgrafischen Objekt

Beitrag von charlytango »

Hallo!

Klassischer Ansatz bei einer Applikation mit MainForm und einem mit Ereignisprozedur erzeugten Objekt (in eine eigene Unit ausgelagert) in dem irgend etwas passiert von dem der Benutzer in Kenntnis gesetzt werden soll. In diesem Fall einfach mit einer neuen Zeile in ein TMemo auf dem MainForm.

Das Mainform in die Objektunit eingebunden und schon kann direkt darauf zugegriffen werden.

Im Bestreben Programmlogik samt deren Wiederverwendbarkeit in ein eigenes Objekt in eine getrennte Unit auszulagern (um sie in anderen Applikationen unverändert weiter verwenden zu können) aber leider keine elegante Lösung.

Ich dachte da an ein Property etc. im Objekt dem ich das TMemo von außen zuweise. Innerhalb des Objektes wird ins Memo geschrieben wenn das entsprechende Property zugewiesen wurde.

Mag sein dass ich da auf dem Holzweg bin, denn das will einfach so nicht klappen. An sich sollte das nicht nur mit einem TMemo sondern evtl auch mit einem Progressbar funktionieren.

Bin für Anregungen zu einer eleganten Lösung dankbar, denn derartige Anforderungen werden sicher nicht neu sein.

Danke im Voraus

CharlyTango

Benutzeravatar
theo
Beiträge: 10467
Registriert: Mo 11. Sep 2006, 19:01

Re: Zugriff auf TMemo von einem nichtgrafischen Objekt

Beitrag von theo »

charlytango hat geschrieben:Mag sein dass ich da auf dem Holzweg bin, denn das will einfach so nicht klappen.

Was bedeutet das?

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Zugriff auf TMemo von einem nichtgrafischen Objekt

Beitrag von mschnell »

TMemo ist ein Nachfolger von TStrings.

Du kannst das Memo z.B. an eine Funktion über einen Parameter mit TStrings übergeben und dort mit den Funktionalitäten von TStrings bearbeiten.

Die Unit mit dieser Funktion braucht dann keine grafischen Units zu usen. (Wenn es das ist, was Du meinst...)

-Michael

Benutzeravatar
m.fuchs
Lazarusforum e. V.
Beiträge: 2636
Registriert: Fr 22. Sep 2006, 19:32
OS, Lazarus, FPC: Winux (Lazarus 2.0.10, FPC 3.2.0)
CPU-Target: x86, x64, arm
Wohnort: Berlin
Kontaktdaten:

Re: Zugriff auf TMemo von einem nichtgrafischen Objekt

Beitrag von m.fuchs »

mschnell hat geschrieben:TMemo ist ein Nachfolger von TStrings.

Das ist Quark. TMemo benutzt TStrings, leitet aber nicht davon ab.

Aber zur eigentlichen Frage. Für maximale Trennung und Wiederverwendbarkeit, bietet sich ein Interface an. Nehmen wir mal an, deine Programmlogik besteht aus einer Klasse TMyService mit der Methode DoSomething. Wird die Methode aufgerufen, dann soll ein Zähler von 0 bis 200 hochzählen. Bei jedem Schritt soll sich ein ProgressBar bewegen und in einem Memo eine neue Zeile hinzugefügt werden. Aber wie du ja schon richtig erkannt hast, ist diese grafische Ausgabe in einem anderen Programm vielleicht völlig anders. Dann gehen wir den Weg über ein Interface:

Code: Alles auswählen

unit Service;
{$MODE ObjFpc}
{$H+}
{$INTERFACES CORBA}
 
interface
 
uses
  Classes, SysUtils;
 
type
  IListener = interface
    procedure Step;
    procedure AddLogline(Line: String);
  end;
 
  TMyService = class(TObject)
    private
      FListener: IListener;
    public
      property Listener: IListener read FListener write FListener;
    public
      procedure DoSomething;
  end;
 
implementation
 
procedure TMyService.DoSomething;
var
  i: Integer;
begin
  for i := 0 to 200 do begin
    Sleep(100);
    FListener.AddLogline('We are in cycle #' + IntToStr(i));
    FListener.Step;
  end;
end;
 
end.


Das Interface kann von jeder beliebigen Klasse implementiert werden, das heißt diese Klasse muss dann die beiden Methoden Step und AddLogline anbieten. Das sieht in einem einfachen Form mit Button, Progressbar und Memo dann so aus:

Code: Alles auswählen

unit Unit1;
 
{$mode objfpc}{$H+}
 
interface
 
uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, ComCtrls,
  Service;
 
type
  TForm1 = class(TForm, IListener)
    Button1: TButton;
    Memo1: TMemo;
    ProgressBar1: TProgressBar;
    procedure Button1Click(Sender: TObject);
  public
    procedure Step;
    procedure AddLogline(Line: String);
  end;
 
var
  Form1: TForm1;
 
implementation
 
{$R *.lfm}
 
procedure TForm1.Button1Click(Sender: TObject);
var
  MyService: TMyService;
begin
  try
    MyService := TMyService.Create;
    MyService.Listener := Self;
    MyService.DoSomething;
  finally
    FreeAndNil(MyService);
  end;
end;
 
procedure TForm1.Step;
begin
  ProgressBar1.StepIt;
  Application.ProcessMessages;
end;
 
procedure TForm1.AddLogline(Line: String);
begin
  Memo1.Append(Line);
  Application.ProcessMessages;
end;
 
end.


Willst du deinen Service dann in einem anderen Programm wiederverwenden, musst du dort nur das Interface implementieren wie oben dargestellt.

Soweit erst einmal der grobe Überblick. Bei Fragen => fragen.
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de

charlytango
Beiträge: 843
Registriert: Sa 12. Sep 2015, 12:10
OS, Lazarus, FPC: Laz stable (2.2.6, 3.x)
CPU-Target: Win 32/64, Linux64
Wohnort: Wien

Re: Zugriff auf TMemo von einem nichtgrafischen Objekt

Beitrag von charlytango »

Erstmal danke für die ausführliche Antwort!

Elegant ist die Lösung allemal, wenngleich etwas mehr Aufwand als ich erwartet habe ;-)

Den vorgeschlagenen Code habe ich ohne Änderung in eine Applikation zum Test umgewandelt.

Ganz klar ist mir der Zusammenhang zwischen dem Mainform und dem Interface nicht.
Woher weiß das Mainform welches Interface zum Tragen kommt?
Bloss durch die Zuweisung

Code: Alles auswählen

 
MyService.Listener := Self;
 


im Mainform und die namensgleichen Funktionen in der public-Sektion des Mainforms?

im übrigen wird die Zeile vom Kompiler bemängelt:

Code: Alles auswählen

Projekt kompilieren, Ziel: testinterface.exe: Exit code 1, Fehler: 1
fmain.pas(39,27) Error: Incompatible types: got "TForm1" expected "IListener"


die Verbindung in der Unit services zum Interface ist klar, indem in TMyService.DoSomething die Interface-Prozeduren aufgerufen werden.
Bloß die Verbindung zum TMemo im Mainform erschließt sich mir nicht -> daher frage ich nochmal um Erklärung nach

LG
Dateianhänge
test_Interface.zip
(1.81 KiB) 63-mal heruntergeladen

Benutzeravatar
m.fuchs
Lazarusforum e. V.
Beiträge: 2636
Registriert: Fr 22. Sep 2006, 19:32
OS, Lazarus, FPC: Winux (Lazarus 2.0.10, FPC 3.2.0)
CPU-Target: x86, x64, arm
Wohnort: Berlin
Kontaktdaten:

Re: Zugriff auf TMemo von einem nichtgrafischen Objekt

Beitrag von m.fuchs »

Da hast du wohl eine Kleinigkeit übersehen:

Code: Alles auswählen

type
  TForm1 = class(TForm, IListener)


Das sagt dem Compiler, dass TForm1 von der Klasse TForm abgeleitet ist und das Interface IListener implementiert (weitere Interfaces könnten komma-getrennt hinzugefügt werden). Das in Verbindung mit den implementierten Methoden erlaubt dir auch die Zuweisung, an der momentan bei dir der Compiler stolpert.
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de

charlytango
Beiträge: 843
Registriert: Sa 12. Sep 2015, 12:10
OS, Lazarus, FPC: Laz stable (2.2.6, 3.x)
CPU-Target: Win 32/64, Linux64
Wohnort: Wien

Re: Zugriff auf TMemo von einem nichtgrafischen Objekt

Beitrag von charlytango »

Von wegen Kleinigkeit :(
da war ich wohl eher blind wie der sprichwörtliche Maulwurf.

Danke für den Hinweis --- somit klappt alles.
Danke für die Hilfe ! Jetzt hab ich Eleganz und Funktion.

Für die Suchenden habe ich den funktionierenden Source nochmal attached.

CASE CLOSED
Dateianhänge
test_Interface.zip
(1.81 KiB) 54-mal heruntergeladen

SchwabenTom
Beiträge: 49
Registriert: So 4. Jan 2015, 21:34
OS, Lazarus, FPC: Winux (L 0.9.xy FPC 2.2.z)
CPU-Target: xxBit

Re: Zugriff auf TMemo von einem nichtgrafischen Objekt

Beitrag von SchwabenTom »

Du kannst mit einer Referenz auf eine Funktion/Methode arbeiten. Beispiele sind OnClick, OnPaint in Formularen.

PS.: Sorry, daß ich dir keinen Code posten kann. Arbeite mich selbst noch in Lazarus ein.

Du hast eine Unit. In dieser Unit definierst du dein TOnProgress. Das wird irgendwas wie ein TNotifyEvent sein - keine Ahnung wie es in Lazarus genau heißt. In dieser Unit hast dann entweder eine globale Variable OnProgress (var OnProgress: TOnProgress;) oder eben als ein Member in deiner Klasse. Du initialisierst es mit nil. Derjenige (bzw. du in deinem "Hauptprogramm"), der deine Unit benutzt, bindet deine Unit mit uses ein. Er definiert in seiner Unit eine Funktion MeldeMirDeinenProgress die vom Typ TOnProgress ist. Dieses MeldeMirDeinenProgress weist er dann der OnProgress-Variable deine Unit zu (bzw. wenn er eine Instanz deiner Klasse erstellt hat, dem betreffenden Member in dieser Klasse/Instanz). So ist die "Verknüpfung" dann erstellt.

Oft ist ein recht gutes Vorgehen sogar, überhaupt keine Parameter zu übergeben. Nur das "Event" wird "ausgelöst". Der Nutzer deiner Unit/Klasse/Framework kennt deine Unit und weiß, wo er sich dann die Daten holen kann, wenn das Event eintritt. Er holt sich dann auch nur das, was er tatsächlich in nur dem jeweiligen Kontext braucht.

In deiner Unit arbeitest du dann so:

if Assigned(FOnProgress) then
FOnProgress(ParameterFallsDuWelcheDefiniertHast);

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Zugriff auf TMemo von einem nichtgrafischen Objekt

Beitrag von mschnell »

m.fuchs hat geschrieben:TMemo benutzt TStrings, leitet aber nicht davon ab.

Sorry für den Irrtum. Die Übergabe der Lines Property an nicht grafische Units (das war ja wohl die Frage) ist davon aber unabhängig.

-Michael

Antworten