Gedankenspiel nonvisueller Grid

Rund um die LCL und andere Komponenten
Antworten
monta
Lazarusforum e. V.
Beiträge: 2809
Registriert: Sa 9. Sep 2006, 18:05
OS, Lazarus, FPC: Linux (L trunk FPC trunk)
CPU-Target: 64Bit
Wohnort: Dresden
Kontaktdaten:

Gedankenspiel nonvisueller Grid

Beitrag von monta »

Es ist nur eine Idee und vielleicht ist der Gedanke auch grundlegend falsch...

Ich persönlich arbeite sehr oft (Da es meist datenbankgeschichten sind) mit Grids, genauer gesagt, meistens mit dem TStringGrid. Ich finde das Stringgrid sowohl zur Darstellung als auch zur Verarbeitung der Daten (Lesen, Schreiben, Speichern usw...) ausgesprochen praktisch und einfach.

Nur es gibt ja auch Daten, die als Tabelle vorliegen (und ein Grid ist ja letztlich nichts anderes) aber eben in diesem Moment nicht dargestellt werden sollen.

Nun würde es sich anbieten, ein nonvisuelles Objekt zu haben, welches sich ähnlich verhält, wie ein Stringgrid, also Rows und Colums, Cells usw.

Nur wie sollte man das realisieren? Gewiss könnte man auch direkt zweidimensionale dynamische Arrays nehmen aber dies setzt natürlich ein völlig anderes Ansprechen voraus, als bei nem Stringgrid.
Gibt es eine Möglichkeit, von TCustomGrid eine nichtvisuelle Klasse abzuleiten, quasi alles was die Bildschirmausgabe angeht zu entfernen?

Oder sollte man eine komplett neue Klasse konstruieren, die intern auf nem 2D dyn. Array aufbaut und nach außen an den Stringgrid angelehnt ist?

Oder gäbe es noch eine völlig andere Variante, ein ähnliches Verhalten nachzuahmen?
Johannes

shokwave
Beiträge: 475
Registriert: Do 15. Nov 2007, 16:58
OS, Lazarus, FPC: Win11/Ubuntu Budgie (L 3.0 FPC 3.2.2)
CPU-Target: i386, x64
Wohnort: Gera

Beitrag von shokwave »

öhm, vielleicht lieg ich auch falsch, aber Visible:=False und gut? ;)
mfg Ingo

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

Beitrag von theo »

Fand deine Idee ganz lustig (obwohl ich nicht weiss wozu ;-)
Hab mal zum entspannen einen Vorschlag gebastelt.
Ist nicht fertig, das kannste selber machen, aber funzt soweit für eine Demo (kann sein dass es noch Bugs gibt, ist ja kaum getestet)
Du kannst auf die Cells zugreifen, ohne vorher Row/ColCount zu setzen.
Auch Zuweisungen innerhalb des TStrings werden bearbeitet.
Also Sg.Rows[5].Text:=....

Ist nur ein Vorschlag.

Code: Alles auswählen

{ TMyStringGrid }
 
  TMyStringGrid=class
  private
    fRows:TList;
    fCols:TStringList;
    fColCount:integer;
    fChangeLock:integer;
    function GetCells(ACol, ARow: Integer): string;
    function GetCols(index: Integer): TStrings;
    function GetRows(index: Integer): TStrings;
    procedure SetCells(ACol, ARow: Integer; const AValue: string);
    procedure SetCols(index: Integer; const AValue: TStrings);
    procedure SetRows(index: Integer; const AValue: TStrings);
    procedure SetColCount(const AValue: Integer);
    procedure SetRowCount(const AValue: Integer);
    function GetColCount: Integer;
    function GetRowCount: Integer;
    procedure DoOnChange(Sender:TObject);
 
  public
    constructor Create;
    destructor Destroy; override;
    property Cells[ACol, ARow: Integer]: string read GetCells write SetCells;
    property Cols[index: Integer]: TStrings read GetCols write SetCols;
    property Rows[index: Integer]: TStrings read GetRows write SetRows;
    property ColCount: Integer read GetColCount write SetColCount;
    property RowCount: Integer read GetRowCount write SetRowCount;
  end;
 
 
implementation
 
{ TMyStringGrid }
 
constructor TMyStringGrid.Create;
begin
  fChangeLock:=0;
  fRows:=TList.Create;
  fCols:=TStringList.create;
end;
 
destructor TMyStringGrid.Destroy;
var i:integer;
begin
  fCols.free;
  for i:=0 to fRows.count-1 do TStringList(fRows[i]).free;
  fRows.free;
  inherited Destroy;
end;
 
 
function TMyStringGrid.GetCells(ACol, ARow: Integer): string;
begin
  inc(fChangeLock);
   if (ARow<RowCount) and (ACol<ColCount) then
     Result:=GetRows(ARow)[ACol];
   dec(fChangeLock);
end;
 
function TMyStringGrid.GetCols(index: Integer): TStrings;
var i:integer;
begin
   fCols.Clear;
   for i:=0 to RowCount-1 do
     fCols.add(GetRows(i)[index]);
   Result:=fCols;
end;
 
function TMyStringGrid.GetRows(index: Integer): TStrings;
begin
  Result:=TStrings(fRows[index]);
end;
 
procedure TMyStringGrid.SetCells(ACol, ARow: Integer; const AValue: string);
begin
 inc(fChangeLock);
 if ACol>ColCount-1 then SetColCount(ACol+1);
 if ARow>RowCount-1 then SetRowCount(ARow+1);
   GetRows(ARow)[ACol]:=AValue;
 dec(fChangeLock);
end;
 
procedure TMyStringGrid.SetCols(index: Integer; const AValue: TStrings);
begin
  //Todo
end;
 
procedure TMyStringGrid.SetRows(index: Integer; const AValue: TStrings);
begin
  //Todo
end;
 
procedure TMyStringGrid.SetColCount(const AValue: Integer);
var i, j:integer;
SL:TStringList;
begin
  inc(fChangeLock);
  fColCount:=AValue;
  for i:=0 to fRows.Count-1 do
    begin
      SL:=TStringList(fRows[i]);
      if SL.Count<AValue then for j:=0 to AValue-SL.Count-1 do SL.Add('');
      if SL.Count>AValue then for j:=0 to SL.Count-AValue-1 do SL.Delete(Sl.count-1);
    end;
  dec(fChangeLock);
end;
 
procedure TMyStringGrid.SetRowCount(const AValue: Integer);
var i,j, Rc:integer;
TempSL:TStringList;
begin
  inc(fChangeLock);
  Rc:=fRows.Count;
  if AValue>Rc then for i:=0 to AValue-Rc-1 do
  begin
    TempSL:=TStringList.create;
    TempSl.OnChange:=@DoOnChange;
    fRows.Add(TempSL);
    for j:=0 to fColCount do TempSL.Add('');
  end;
  if AValue<Rc then for i:=0 to Rc-AValue-1 do
  begin
    TStringList(fRows[FRows.Count-1]).free;
    fRows.Delete(FRows.Count-1);
  end;
  dec(fChangeLock);
end;
 
function TMyStringGrid.GetColCount: Integer;
begin
  Result:=fColCount;
end;
 
function TMyStringGrid.GetRowCount: Integer;
begin
  Result:=fRows.Count;
end;
 
procedure TMyStringGrid.DoOnChange(Sender: TObject);
var TempSL:TStringList;
begin
 if fChangeLock=0 then
 begin
  TempSL:=TStringList(Sender);
  if TempSL.Count>ColCount then SetColCount(TempSl.Count);
  //writeln('change');
 end;
end;
 
procedure TForm1.Button1Click(Sender: TObject);
var Sg:TMyStringGrid;
var i,j:integer;
begin
Sg:=TMyStringGrid.Create;
Sg.Cells[0,0]:='A0';
Sg.Cells[1,1]:='B1';
Sg.Cells[2,2]:='C2';
Sg.Cells[3,3]:='D3';
Sg.Cells[5,4]:='E5';
Sg.Cells[0,5]:='A5';
Sg.Cells[2,0]:='C0';
Sg.Rows[5].Text:='F0'+LineEnding+'F1'+LineEnding+'F3'+LineEnding+'F4'+LineEnding+'F5'+LineEnding+'F6'+LineEnding+'F7';
 
write('Rows: ',Sg.RowCount);
writeln(' Cols: ',Sg.ColCount);
writeln('--grid-');
for i:=0 to Sg.RowCount-1 do
  begin
    for j:=0 to Sg.ColCount-1 do write(Sg.Cells[j,i]+' ');
    writeln('');
  end;
 
writeln('--cols-');
writeln(Sg.Cols[0].text);
writeln('--rows-');
writeln(Sg.Rows[0].text);
 
Sg.free;
end;

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6857
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Beitrag von af0815 »

Nicht auch möglich/sinnvoll vom 'TVirtualGrid' aus der Grids weg zu arbeiten ?!

Bezüglich Sinnhaftigkeit - jeder kennt das StringGrid - ist eine prima Komponente, nur mit viele Overhead, wenn man die nicht visuell benutzt. Deshalb finde ich, kann eine nonvisual schon deutliche Vorteile haben.

Mein Vorschlag wäre, sie so kompatibel wie möglich zu machen, nur halt ohne den sichtbaren Teil. Vor allen, welche Methoden und Eigenschaften werden benötigt ?!
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Christian
Beiträge: 6079
Registriert: Do 21. Sep 2006, 07:51
OS, Lazarus, FPC: iWinux (L 1.x.xy FPC 2.y.z)
CPU-Target: AVR,ARM,x86(-64)
Wohnort: Dessau
Kontaktdaten:

Beitrag von Christian »

Wozu brauch man sowas ?
W.m.k.A.h.e.m.F.h. -> http://www.gidf.de/

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6857
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Beitrag von af0815 »

als dynamischer 2D Behälter mit dem Komfort vom Stringgrid, schon klar, alternativen sind das StringGrid (lol) und das TMemDataset. Oder gibts noch weitere 2D Datenbehälter ?
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Christian
Beiträge: 6079
Registriert: Do 21. Sep 2006, 07:51
OS, Lazarus, FPC: iWinux (L 1.x.xy FPC 2.y.z)
CPU-Target: AVR,ARM,x86(-64)
Wohnort: Dessau
Kontaktdaten:

Beitrag von Christian »

naja ne spezifisch angepasste klasse kostet einem keine 5 min.
und mit nem dynamischen array ist das in 1 min erledigt.
was ist denn am stringgrid so toll ?
W.m.k.A.h.e.m.F.h. -> http://www.gidf.de/

monta
Lazarusforum e. V.
Beiträge: 2809
Registriert: Sa 9. Sep 2006, 18:05
OS, Lazarus, FPC: Linux (L trunk FPC trunk)
CPU-Target: 64Bit
Wohnort: Dresden
Kontaktdaten:

Beitrag von monta »

shokwave hat geschrieben:öhm, vielleicht lieg ich auch falsch, aber Visible:=False und gut? ;)
So war das nicht gemeind, denn, wie af schon schreibt, das beseitigt ja nicht den Overhead den visuellen Sachen dennoch mitbringen.


Af hat genau mein Anliegen beschrieben, ein 2D-Container mit Stringgridkomfort, der optimaler Weise direkt ein Stringgrid ersetzen kann, ohne das die Bezeichner usw. umgeschrieben werden müssen...folglich sollten alle Properties, die relevant sind, die gleichen Bezeichner tragen.
Ob man nun alle Eigenschaften übernimmt, sei mal dahingestellt.

af hat geschrieben:'TVirtualGrid'
Muss ich mir mal anschauen.

Ansonsten geht theos Vorschlag ja schon schön in die Richtung and lässt sich ausbauen.
Christian hat geschrieben:Ne Spezifisch angepasste Klasse...
? Genau das solls doch werden, ne Kalsse die sofort mit nem Stringgrid getauscht werden könnte, ohne erst große Veränderungen an den Zugreifenden Funktionen durchführen zu müssen.
Johannes

monta
Lazarusforum e. V.
Beiträge: 2809
Registriert: Sa 9. Sep 2006, 18:05
OS, Lazarus, FPC: Linux (L trunk FPC trunk)
CPU-Target: 64Bit
Wohnort: Dresden
Kontaktdaten:

Beitrag von monta »

Ich bastel mir gerade ne Klasse zusammen, aber wie kann ich erreichen, das auf das setzen einer TStrings-Eigenschaft reagiert wird? Denn wie soll ich darauf reagieren, wenn die entsprechende Prozedure niemals aufgerufen wird?

Code: Alles auswählen

TNoViGrid=class
  private
    Grid: Array of Array of string;
    fRows:TStringList;
    fCols:TStringList;
 
    procedure SetRows(index: Integer; const AValue: TStrings);
    function GetRows(index: Integer): TStrings;
  public
    property Rows[index: Integer]: TStrings read GetRows write SetRows;
  end;
 
//...
 
procedure TNoViGrid.SetRows(index: Integer; const AValue: TStrings);
var i: integer;
begin
  ShowMessage('Test');
  //^^wird bei Zuweisung an Rows[] nicht ausgeführt
end;
Johannes

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

Beitrag von theo »

monta hat geschrieben:Ich bastel mir gerade ne Klasse zusammen, aber wie kann ich erreichen, das auf das setzen einer TStrings-Eigenschaft reagiert wird? Denn wie soll ich darauf reagieren, wenn die entsprechende Prozedure niemals aufgerufen wird?

Klappt bei mir problemlos;
Grid.Rows[0]:=Memo1.lines;

Wieso machste jetzt alles nochmal neu?
Was ist denn an meinem Vorschlag oben nicht gut?

monta
Lazarusforum e. V.
Beiträge: 2809
Registriert: Sa 9. Sep 2006, 18:05
OS, Lazarus, FPC: Linux (L trunk FPC trunk)
CPU-Target: 64Bit
Wohnort: Dresden
Kontaktdaten:

Beitrag von monta »

Der vorschlag ist gut, ich wolltee s nur nochmal mit dem dyn. Array probieren.

Aber irgendwie hat ich eh nen Denkfehler, dein Code funktioniert bestens, wie ich gerade festgestellt hab. Keine Ahnung, was ich die Nacht falsch gemacht hab. Ich hab mich davon täuschen lassen, das SetRows und SetColums bei dir nicht belegt ist. der Irrtum lag am Denkfehler kein Set... also wird die Eigenschaft nicht übernohmen.

Aber wann wird nun eigentlich wirklich SetCols/SetRows aufgerufen?

(Ich nehm mal an, ich darf den Code so auch übernehmen?)
Johannes

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

Beitrag von theo »

monta hat geschrieben: Aber wann wird nun eigentlich wirklich SetCols/SetRows aufgerufen?
Wie oben gesagt: z.B. Grid.Rows[0]:=Memo1.lines; ruft SetRows auf, oder nicht?
Kannst du mal ein Beispiel machen, wo man dein Problem damit sehen kann?

Wenn du den machst: Grid.Rows[0][3]:='AHA'; wird natürlich ein GetRows gemacht, kein SetRows!.
Von dieser Änderung kriegt du über DoOnChange Wind.
Besser wäre natürlich hier das Cells Property zu verwenden.
monta hat geschrieben: (Ich nehm mal an, ich darf den Code so auch übernehmen?)
Du musst dich erst bei mir als Code-User registrieren.
(Mädchennamen BEIDER Grossmütter nicht vergessen!)
Danach kannst du den Code aktivieren lassen fur 5 Benutzungen.
Bitte HD und Netzwerkarten-ID sowie Carosserienummer deines Computers angeben.
Wenn du den Code auf einem anderen Rechner ausführen wilst, bitte mich telefonisch um eine Erhöhung des Benutzungs-limits anflehen.

Achso, bin ja weder MS noch Codegear.
Dann.... Ja kannste übernehmen. ;-)

monta
Lazarusforum e. V.
Beiträge: 2809
Registriert: Sa 9. Sep 2006, 18:05
OS, Lazarus, FPC: Linux (L trunk FPC trunk)
CPU-Target: 64Bit
Wohnort: Dresden
Kontaktdaten:

Beitrag von monta »

Wie oben gesagt: z.B. Grid.Rows[0]:=Memo1.lines; ruft SetRows auf, oder nicht?
Nein, oder wir reden aneinander vorbei.

Code: Alles auswählen

procedure TNVStringGrid.SetRows(index: Integer; const AValue: TStrings);
begin
  //Todo
  WriteLn('Test');
end;
Bspw. bei folgendem Code müsste doch SetRows aufgerufen werden (oder hab ich da einen Grundlegenden Verständnisfehler?):

Code: Alles auswählen

Grid.Rows[i].DelimitedText := Liste.Strings[i];
aber es wird nichts aufgerufen und Test erscheint nicht in der Konsole.


Und es gibt noch ein seltsames Phänomen:

Code: Alles auswählen

procedure TForm1.Button1Click (Sender: TObject );
var Grid: TNVStringGrid;
    Liste: TStringList;
    i: integer;
    Count: integer;
begin
  //...
    Liste.LoadFromFile('xyz');
    Grid.ColCount := 4;
//>>
    count := Liste.Count;
    ShowMessage(inttostr(Count));
    Grid.RowCount := Count;// 362;
    ShowMessage(inttostr(Count));
    for i := 0 to {361 do } Count - 1 do;
//<<
    begin
      Grid.Rows[i].Delimiter := ';';
      Grid.Rows[i].QuoteChar := '"';
      Grid.Rows[i].DelimitedText := Liste.Strings[i];
    end;
    Grid.WriteContentToShell;
  //...
end;
Die Message gibt exakt 362 aus, was auch korrekt ist. Übergeb ich diesen Wert mittels der Variablen Count wie oben, bleiben alle Einträge bis auf den letzten leer.
Ersetze ich Count durch den konkreten Wert 362 werden alle Zeilen korrekt in den Grid übernohmen und in der Konsole ausgegeben.

TNVStringGrid ist exakt Theos Klasse, mit der Ergänzung von WriteContentToShell.
Johannes

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

Beitrag von theo »

monta hat geschrieben:
Nein, oder wir reden aneinander vorbei.
...
Bspw. bei folgendem Code müsste doch SetRows aufgerufen werden (oder hab ich da einen Grundlegenden Verständnisfehler?):

Code: Alles auswählen

Grid.Rows[i].DelimitedText := Liste.Strings[i];
Ich hab dich schon verstanden. SetRows wird nur so aufgerufen: Grid.Rows[0]:=Memo1.lines;
In deinem Code HOLST du Grid.Rows (also eigentlich den Pointer auf TStringList) und machst was damit. Das ist kein SetRows sondern ein GetRows!

Eigentlich genau wie:
TempSL:=Grid.Rows;
TempSL.DelimitedText:=....

und nicht
Grid.Rows:=....

Wie ich schon geschrieben habe, hänge ich deswegen auch:

Code: Alles auswählen

TempSl.OnChange:=@DoOnChange;
ein, damit man von sowas Nachricht kriegt.

monta
Lazarusforum e. V.
Beiträge: 2809
Registriert: Sa 9. Sep 2006, 18:05
OS, Lazarus, FPC: Linux (L trunk FPC trunk)
CPU-Target: 64Bit
Wohnort: Dresden
Kontaktdaten:

Beitrag von monta »

Danke, mit Zeigern tu ich mich in der Praxis immer schwer.
Johannes

Antworten