Dziennik lekcyjny - problem z zapisem/odczytem kontrolki do pliku

0

Piszę program, który ma służyć za dziennik lekcyjny. Oceny, sprawdzanie obecności, uwagi i ma mieć zakładkę dla każdego przedmiotu,wczytuje i zapisuje,czyści wszystkie komórki,dodaje i usuwa zakładki. Jednak utknąłem i nie wiem co robić mam problem z zapisaniem pagecontrol stringgrida do pliku, odczytaniem tego i działaniem klawiszy na otwartych zakładkach.

Oto kod

unit dziennikLekcyjnyGP;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, Menus, StdCtrls, ComCtrls, TabNotBk, Grids, ExtCtrls, Tabs, ExtDlgs,ShellAPI;

type
  TForm1 = class(TForm)
    MainMenu: TMainMenu;
    StringGrid1: TStringGrid;
    Plik1: TMenuItem;
    Koniec2: TMenuItem;
    Nowy1: TMenuItem;
    Open: TOpenTextFileDialog;
    Save: TSaveTextFileDialog;
    Zapisz1: TMenuItem;
    Wczytaj1: TMenuItem;
    Obecnosc1: TMenuItem;
    Srednia1: TMenuItem;
    PageControl1: TPageControl;
    Dodajzak1: TMenuItem;
    Zamknijzak1: TMenuItem;
    Edit1: TEdit;
    procedure Koniec2Click(Sender: TObject);
    procedure Wczytaj1Click(Sender: TObject);
    procedure Zapisz1Click(Sender: TObject);
    procedure Nowy1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure Dodajzak1Click(Sender: TObject);
    procedure Zamknijzak1Click(Sender: TObject);
    procedure Srednia1Click(Sender: TObject);
    procedure Obecnosc1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  StringGrid:TStringGrid;

implementation
  {$R *.dfm}


procedure wypelnij_tabele(var s:TstringGrid); //przypisuje nazwy do komurek
var i:integer;
begin
  with s do begin
  Cells[1,0]:='Imie';
  Cells[0,1]:='Daty lekcji';
  Cells[2,0]:='Nazwisko';
  Cells[3,0]:='Oceny';
  Cells[12,0]:='Średnia';
  Cells[13,0]:='Obecnosc';
  Cells[33,0]:='Uwagi';
  for i:=1 to 101 do
  begin
  Cells[13,i+1]:='Obecny';
  Cells[14,i+1]:='Obecny';
  Cells[15,i+1]:='Obecny';
  Cells[16,i+1]:='Obecny';
  Cells[17,i+1]:='Obecny';
  Cells[18,i+1]:='Obecny';
  Cells[19,i+1]:='Obecny';
  Cells[20,i+1]:='Obecny';
  Cells[21,i+1]:='Obecny';
  Cells[22,i+1]:='Obecny';
  Cells[23,i+1]:='Obecny';
  Cells[24,i+1]:='Obecny';
  Cells[25,i+1]:='Obecny';
  Cells[26,i+1]:='Obecny';
  Cells[27,i+1]:='Obecny';
  Cells[28,i+1]:='Obecny';
  Cells[29,i+1]:='Obecny';
  Cells[30,i+1]:='Obecny';
  Cells[31,i+1]:='Obecny';
  Cells[32,i+1]:='Obecny';
  end;
  for i:=1 to 101 do
  cells[0,i+1]:=IntToStr(i);
  end
end;

procedure TForm1.Dodajzak1Click(Sender: TObject); //Dodaje zakladke
var
  TabSheet: TTabSheet;
  nazwa:string;
  tab:string;

begin                  //Okienko z pyt o nazwe zakłądki i tworzy stringgrid w pagecontrol
  Edit1.Text:=InputBox('Zakładki','Podaj nazwe zakłądki:','Tu wpisz nazwe zakładki');
  nazwa:=(Edit1.Text);
  TabSheet := TTabSheet.Create(PageControl1);
  TabSheet.Caption := nazwa;
  TabSheet.PageControl := PageControl1;
  StringGrid := TStringGrid.Create (TabSheet);
  StringGrid.ColCount := 50;
  StringGrid.Align:=alClient;
  StringGrid.ScrollBars:=ssBoth;
  StringGrid.Options := StringGrid.Options + [goThumbTracking];
  StringGrid.Options:= StringGrid.Options + [goEditing];
  StringGrid.Options:= StringGrid.Options + [goFixedRowClick];
  StringGrid.Options:= StringGrid.Options + [goFixedColClick];
  StringGrid.Options:= StringGrid.Options + [goTabs];
  StringGrid.Options:= StringGrid.Options + [goRowSizing];
  StringGrid.Options:= StringGrid.Options + [goColSizing];
  StringGrid.Parent := TabSheet;
  wypelnij_tabele(StringGrid);
  StringGrid.RowCount := 101;
  StringGrid.Visible := True;
  end;

procedure TForm1.FormCreate(Sender: TObject);  //jest uzywana na onCreat i po nacisknieci nowy
begin                                          //wywoluje procedure wypelnij_tabele
  wypelnij_tabele(StringGrid1);

end;


procedure TForm1.Koniec2Click(Sender: TObject);  //zamyka program
begin
  Close;
end;

procedure TForm1.Nowy1Click(Sender: TObject);   //czysni stringrid i wywoluje procedure wypelnij_tabele
  var i : integer;
begin
  PageControl1.ActivePage.TabIndex;
  for i:=0 to stringgrid.ColCount do
    begin
      stringgrid.Cols[i].Clear;
    end;
  for i:=0 to stringgrid.RowCount do
    begin
      stringgrid.Rows[i].Clear;
    end;
  wypelnij_tabele(StringGrid);
end;



procedure TForm1.Obecnosc1Click(Sender: TObject);  //wpisuje nieobennosc do komurki z wybramym nazwiskiem
var
nazwisko,data: String;
i:Integer;
begin
  PageControl1.ActivePage.TabIndex;
  Edit1.Text:=InputBox('Nie obecni','Podaj nazwisko nieobecnej osoby:','Nazwisko');
  nazwisko:=(Edit1.Text);
  Edit1.Text:=InputBox('Nie obecni','Podaj date nieobecnosci:','Data');
  data:=(Edit1.Text);
  for i:=1 to StringGrid.RowCount do
  begin
  if StringGrid.Cells[0,i+15]=data then
    if StringGrid.Cells[2,i]=nazwisko then
      begin
      StringGrid.Row:=i;//zaznaczanie komorki w danym wierszy
      StringGrid.Col:=1;//zaznaczanie komorki w danej kolumnie
      StringGrid.Cells[13,i]:='Nie obecny';
      end;
    end;
end;


procedure TForm1.Srednia1Click(Sender: TObject); //oblicza srednia ocen
var
  i:Integer;
  A,B,C,D,E,F,G,H,J:real;
begin
  PageControl1.ActivePage.TabIndex;
  ShowMessage('Oblicza srednia dla 9 ocen');
  for i:=1 to StringGrid.RowCount-1 do
    begin
      A:=StrTofloat(StringGrid.Cells[3,i]);
      B:=StrTofloat(StringGrid.Cells[4,i]);
      C:=StrTofloat(StringGrid.Cells[5,i]);
      D:=StrTofloat(StringGrid.Cells[6,i]);
      E:=StrTofloat(StringGrid.Cells[7,i]);
      F:=StrTofloat(StringGrid.Cells[8,i]);
      G:=StrTofloat(StringGrid.Cells[9,i]);
      H:=StrTofloat(StringGrid.Cells[10,i]);
      J:=StrTofloat(StringGrid.Cells[11,i]);
      StringGrid.Cells[12,i]:=floatToStr((A+B+C+D+E+F+G+H+J)/9);
    end;
 end;

procedure TForm1.Wczytaj1Click(Sender: TObject);   //wczytuje stringgrid po kolumn i wierszy
var
  f:textfile;
  temp,x,y:integer;
  tempstr:string;
begin
  if open.Execute then
  begin
    if FileExists(open.FileName) then
    assignfile (f,open.filename);
    reset (f);
    readln (f,temp);
    stringgrid1.colcount:=temp;
    readln (f,temp);
    stringgrid1.rowcount:=temp;
    For X:=0 to stringgrid1.colcount-1 do
      For y:=0 to stringgrid1.rowcount-1 do
      begin
        readln (F, tempstr);
        stringgrid1.cells[x,y]:=tempstr;
      end;
    closefile (f);
  end;
end;

procedure TForm1.Zamknijzak1Click(Sender: TObject); //usuwa zakłądke
begin
  PageControl1.ActivePage.Free;
end;

procedure TForm1.Zapisz1Click(Sender: TObject);   //zapisuje tekst z stringgrid to kol i wirszy
var
f:textfile;
x,y,i:integer;
Stringgrid:Tstringgrid;
begin
 for i := 0 to PageControl1.ActivePage.ControlCount  -1 do
  if PageControl1.ActivePage.Controls[i] is TStringGrid then
   begin
   StringGrid := PageControl1.ActivePage.Controls[i] as TStringGrid;
   PageControl1.ActivePage.Caption := Save.Filename;
   end;
     if Save.Execute then
      assignfile (f,save.filename);
      rewrite (f);
      writeln (f,stringgrid1.colcount);
      writeln (f,stringgrid1.rowcount);
      For X:=0 to stringgrid1.colcount-1 do
        For y:=0 to stringgrid1.rowcount-1 do
          writeln (F, stringgrid1.cells[x,y]);
          closefile (f);
end;
end.

dodanie znacznika <code class="delphi"> - fp

3

Ten kod nadaje sie do przepisania tylko i wyłącznie

  for i:=1 to 101 do
  begin
  Cells[13,i+1]:='Obecny';
  Cells[14,i+1]:='Obecny';
  Cells[15,i+1]:='Obecny';
  Cells[16,i+1]:='Obecny';
  Cells[17,i+1]:='Obecny';
  Cells[18,i+1]:='Obecny';
  Cells[19,i+1]:='Obecny';
  Cells[20,i+1]:='Obecny';
  Cells[21,i+1]:='Obecny';
  Cells[22,i+1]:='Obecny';
  Cells[23,i+1]:='Obecny';
  Cells[24,i+1]:='Obecny';
  Cells[25,i+1]:='Obecny';
  Cells[26,i+1]:='Obecny';
  Cells[27,i+1]:='Obecny';
  Cells[28,i+1]:='Obecny';
  Cells[29,i+1]:='Obecny';
  Cells[30,i+1]:='Obecny';
  Cells[31,i+1]:='Obecny';
  Cells[32,i+1]:='Obecny';
  end;
  for i:=1 to 101 do
  cells[0,i+1]:=IntToStr(i);

zamienić na

 for i:=2 to 102 do
 begin
  for j:=13 to 32 do
  begin
   Cells[j,i]:='Obecny';
  end;
  cells[0,i]:=IntToStr(i);
 end;

tutaj jest błąd

  wypelnij_tabele(StringGrid);
  StringGrid.RowCount := 101;

Najpierw wywołujesz procedurę która przelatuje przez wszystkei wiersze (domyslnie 101) a potem dopiero ustawiasz ilosc wierszy na 101.

A co do pytania z tematu. Musisz opracować jakiś format danych w pliku. Zrzut rozumiem, że musiałby się tyczyć wszystkich zakładek, czyli musiałbyś zapisać nazwę zakładki i zawartośc stringgrida z danej zakładki
np:

[TAB]
Nazwa zakładki
1;2;3;4;5;6;7
8;9;0;11;12;13
[/TAB]
[TAB]
Nazwa zakładki2
1;2;3;4;5;6;7
8;9;0;11;12;13
[/TAB]

Tutaj też jest błąd:

if Save.Execute then
      assignfile (f,save.filename);
      rewrite (f);
      writeln (f,stringgrid1.colcount);
      writeln (f,stringgrid1.rowcount);
      For X:=0 to stringgrid1.colcount-1 do
        For y:=0 to stringgrid1.rowcount-1 do
          writeln (F, stringgrid1.cells[x,y]);
          closefile (f);

Sprawdzasz warunek i do niego zależna jest tylko pierwsza linia po ifie, brakuje begin/end

if Save.Execute then
begin
 assignfile (f,save.filename);
 rewrite (f);
 writeln (f,stringgrid1.colcount);
 writeln (f,stringgrid1.rowcount);
 For X:=0 to stringgrid1.colcount-1 do
 For y:=0 to stringgrid1.rowcount-1 do
 writeln (F, stringgrid1.cells[x,y]);
 closefile (f);
end;

Ech, zacząłem czytać, i ten kod jest pełny bugów. Sprawdź ogólnie warunki i bloki kodu które są od nich zależne. Po drugie - poprawe formatowanie, bo mozna się pogubić

Ehh..

procedure TForm1.Srednia1Click(Sender: TObject); //oblicza srednia ocen
var
  i:Integer;
  A,B,C,D,E,F,G,H,J:real;
begin
  PageControl1.ActivePage.TabIndex;   //po co to w ogóle? do czego to służy?
  ShowMessage('Oblicza srednia dla 9 ocen');
  for i:=1 to StringGrid.RowCount-1 do
    begin
      A:=StrTofloat(StringGrid.Cells[3,i]);
      B:=StrTofloat(StringGrid.Cells[4,i]);
      C:=StrTofloat(StringGrid.Cells[5,i]);
      D:=StrTofloat(StringGrid.Cells[6,i]);
      E:=StrTofloat(StringGrid.Cells[7,i]);
      F:=StrTofloat(StringGrid.Cells[8,i]);
      G:=StrTofloat(StringGrid.Cells[9,i]);
      H:=StrTofloat(StringGrid.Cells[10,i]);
      J:=StrTofloat(StringGrid.Cells[11,i]);
      StringGrid.Cells[12,i]:=floatToStr((A+B+C+D+E+F+G+H+J)/9);
    end;
 end;

na

procedure TForm1.Srednia1Click(Sender: TObject); //oblicza srednia ocen
var
 i, j:Integer;
 sum:real;
begin
 ShowMessage('Oblicza srednia dla 9 ocen');
 for i:=1 to StringGrid.RowCount-1 do
 begin
  sum:=0;
  for j:=0 to 8 do
  sum:=sum+StrTofloat(StringGrid.Cells[j+3,i]);
  StringGrid.Cells[12,i]:=floatToStr(sum/9);
 end;
end;
0

Dzięki.

Czy można zapisać to za pomocą TFileStream.WriteComponent ??

I może jakaś pomocna komenda które pozwala ma prace buttona na aktualnie otwartej zakładce.

PageControl1.ActivePage.TabIndex; nie służy do wywoływanie zdarzenie np nacieknięcia buttona średnia na aktualnej stronie pagecontrol?
Bo szukając pomocy znalazłem coś takiego i faktycznie działało ale tylko dla aktualnej 1 zakładki .

0

...służy do wywoływanie zdarzenie np nacieknięcia buttona średnia na aktualnej stronie pagecontrol?...

?????????

0

PageControl1.ActivePage.TabIndex;
To jest liczba całkowita, nie robi zupełnie nic.
To tak samo jakbyś napisał

procedure foo;
var
 licznik:integer;
begin
 licznik;
end;

Możesz tą liczbę przypisać do jakiejś zmiennej, np

procedure foo;
var
 licznik:integer;
begin
 licznik:=PageControl1.ActivePage.TabIndex;
 ShowMessage(IntToStr(licznik));
end;

W takiej postaci jak to napisałeś, to jest ona zupełnie niepotrzebna.

0

Jeszcze raz dzięki.

0

Stricte, to nie jest liczba - jest to property wywołujące wirtualną metodę GetPageIndex, czyli nie jest to tym samym, co napisanie po prostu zmienna; :P Btw, *tę liczbę.

@Patryk27 : Założeniem property jest używanie tego jako zwykłej zmiennej, więc komplikujesz sprawę. Wg. twojej metody kursy Pascala powinno się zaczynać od budowy kompilatora oraz znajomości możliwych plików wykonywalnych oraz assemblerów. Natomiast śmiem zakładać że nawet ty tyle nie wiesz, więc może po prostu KISS?

2

Zauważ, że nie jest to dział "newbie", i podobnie, jak jest wyraźna różnica pomiędzy napisaniem:

Const I = 10;

oraz

Const I: Integer = 10;

Istnieje różnica pomiędzy:

Var I: Integer;
Begin
 I;
End;

A:

Begin
 TMyClass.SomeProperty;
End;

:P

0
payl napisał(a)

Założeniem property jest używanie tego jako zwykłej zmiennej, więc komplikujesz sprawę.

Nie, założeniem property jest "kontrola" modyfikacji/odczytu wartości pola przez ustalenie odpowiedniej metody dostępowej i zmieniającej, bądź w przypadku właściwości prostej bezpośrednich połączeń z polem; Na dodatek właściwości od zwykłych zmiennych odróżnia to, że podczas modyfikacji czy odczytu wartości pola można wykonać automagicznie inne dodatkowe czynności w ww. metodach niedostępne programiście wykorzystujęcemu daną klasę;

Poza tym właściwość danej klasy może działać na różne sposoby:

  • jako zmienna (setter i getter) - możliwość zapisu oraz odczytu + opcjonalnie dodatkowe czynności,
  • jako stała (tylko getter) - możliwość jedynie odczytu + opcjonalnie dodatkowe czynności,
  • jako dziwoląg (tylko setter) - możliwość jedynie zapisu + opcjonalnie dodatkowe czynności;
    Tak więc właściwości nie są zwykłymi zmiennymi i nie można ich tak traktować, a tym bardziej nie można stwierdzić, że takie było założenie o jakim piszesz;

PS: Co do trzeciej opcji - "jako dziwoląg" - nie wiedziałem jak to nazwać, jednak jest taka możliwość zarówno w Delphi, jak i FPC.

0

A co z działaniem klawiszy na stringgrid w pagecontrol, np mam przycisk "Nowy" który czyści stringgrid ale działa on tylko na jednym stringgridzie, a potem nie chce działać ponownie i może ktoś dać jakaś podpowiedz to procedury "Obecnosc" ma ona działać tak, "program pyta o nazwiska osoby, nastepnie program pyta o datę nieobecności po podaniu tych 2 niewidomych program zmienia w komórce obecność na nie obecność.

0
procedure TForm1.Srednia1Click(Sender: TObject); //oblicza srednia ocen
var
 i, j:Integer;
 sum:real;
begin
 ShowMessage('Oblicza srednia dla 9 ocen');
 for i:=1 to StringGrid.RowCount-1 do
 begin
  sum:=0;
  for j:=0 to 8 do
  sum:=sum+StrTofloat(StringGrid.Cells[j+3,i]);
  StringGrid.Cells[12,i]:=floatToStr(sum/9);
 end;
end;

ok ok ale to jest dla samego stringgrid a mój stringgrid jest tworzony w pagecontrol z tego co zauważyłem to nie mogę tego wywołać jak dla zwykłego stringgrida albo nie działa w ogóle albo tylko raz jak inne przyciski które powinny coś robić na tym stinggridzie.

dodanie znacznika <code class="delphi"> - fp

ps To kod jak i cały projekt jest zamieszczony no początku.

0

np mam przycisk "Nowy" który czyści stringgrid ale działa on tylko na jednym stringgridzie

No działa tak, jak jest napisany; Odwołujesz się statycznie do jednej kontrolki StringGrid;

Jeśli chcesz mieć to zdarzenie wspólne dla wszystkich gridów - musisz dynamicznie pobierać referencję do odpowiedniego StringGrid i dopiero na nim wykonywać operacje;

PS: Pamiętaj o wstawianiu kodu w odpowiednie znaczniki kolorujące składnię.

0

Ok, dzięki a możecie mi powiedzieć czy opłaca się robić zapis TFileStream.WriteComponent, czy działa on dla stringgrida umieszczonego w pagecontrolu.

0

Jeśli poprawnie się z niego korzysta to nie powinno być z tym problemu; Może nawet da się całego PageControl zapisać do pliku - nie wiem, ale spróbuję i dam znać; Ty też spróbuj;


EDIT: Niestety nie udało mi się poprawnie zapisać całego PageControl do pliku; Raczej nie ma większego sensu kombinować w ten sposób; W sieci jest dużo pytań właśnie o zapis/odczyt StringGrid do/z pliku i w większości wątków przedstawiony jest zapis kontrolki za pomocą dwóch zagnieżdżonych pętli przechodzącej przez każdą komórkę;

Można łatwo napisać procedury do zapisu/odczytu grida do/z pliku wykorzystując zwykłe pliki tekstowe; Trochę więcej roboty będzie przy wykorzystaniu plików amorficznych, ale jeśli chcesz uniknąć możliwości edycji takiego pliku w dowolnym wdytorze tekstowym to można z nich skorzystać;

Nie jest też problemem zapisanie wszystkich gridów do jednego pliku amorficznego; Jednak trzeba dobrze to przemyśleć i w nagłówku pliku zapisać informacje o ilości gridów, potem dla każdego grida zapisać informacje o ilości wierszy i kolumn, a potem w pętli zapisywać ilość znaków łańcucha dla danej komórki i sam łańcuch jako jej wartość; Wbrew pozorom nie jest to takie trudne, ale trzeba nieco na ten temat poczytać, jeśli się wcześniej tego nie robiło;


Furious Programming napisał(a)

Jeśli chcesz mieć to zdarzenie wspólne dla wszystkich gridów - musisz dynamicznie pobierać referencję do odpowiedniego StringGrid i dopiero na nim wykonywać operacje;

A co do tej kwestii - nie wiem dokładnie co chcesz zrobić, ale jeśli potrzebujesz wykorzystać jedną procedurę do operacji na aktywnym gridzie, to musisz go najpierw znaleźć przez odczytanie indeksu aktywnej karty w PageControl a potem samą kontrolkę; Jeśli już będziesz miał referencję do odpowiedniej kontrolki - możesz wykonać na niej odpowiednie operacje.

0

Ja tylko doradze, że skoro to nie dział Newbie. Pytającemu polecam skorzystać z własności Objects danej komórki StringGrid i stworzyć sobie klasę do przechowywania wszelkich informacji o uczniu. Następnie zapisywać to do obiektu w kolumnie z nazwiskami (począwszy od pierwszego nazwiska, czyli pewnie wiersz o indeksie 1), przy odczycie dodawać do StrigGrida. W tym typie, a najlepiej klasie dziedziczącej poTObject później zapisać sobie TMemory/TFileStreamem kolejno dane do pliku. Jeżeli chcemy mieć dane zabezpieczone przed modyfikowaniem ręcznym można plik spakować ZLibem albo prosto zaszyfrować choćby xor'em czy szyfrem Cezara. Inne rozwiązanie to zapis do pliku typu XML. Jednak według mnie zapisywanie do pliku typowanego jest łatwiejsze. Tylko wiadomo jeżeli zapisujemy stringi to albo ograniszamy długośc pól, ale lepiej według mnie "zmarnowac" dodatkowe cztery bajty i zapisywać wcześniej długośc tekstu we własnościach zawierających takowy tekst. W google znajdziesz potrzebne przykłady na wspomniane przeze mnie rozwiązanie. Na pewno o wiele lepsze niż kombinowanie z zapisaniem komponentu lub całek zakładki.

0

Dzięki.
Jeżeli macie jeszcze jakieś pomysły albo części kody tez z chęcią przyjmę.

0

@Greho - przyjąć każdy przyjmie, tylko kto je napisze? :]

Teraz jedyne co Ci pozostaje to testować różne rozwiązania, jakie zostały zaproponowane; Jeśli będziesz miał problem z kodem (a nie z jego brakiem) to napisz - pomoże się go usprawnić.

0

To może chociaż jakieś pomocne komendy.

0

Gotowca nie dostaniesz - taka jest zasada tego forum, jednak przykładowy algorytm mogę przedstawić;


Poniżej przykład zapisu do i odczyt z pliku amorficznego; Algorytm jest na tylko "ubogi", że:

  • zapisuje jedynie krótkie łańcuchy ShortString - aby to zmienić musisz użyć innego typu dla długości każdej wartości komórki niż Byte,
  • nie obsługuje szyfrowania - zawartość pliku można łatwo odczytać i zmodyfikować w dowolnym HexEdytorze,
  • zapisuje zawartość jedynie jednego StringGrid - aby rozszerzyć go o zapis zawartości dowolnej ilości gridów należy go nieco zmodyfikować,
  • zapis obejmuje jedynie zawartości komórek - żadnych dodatkowych informacji prócz ilości kolumn i wierszy, które są niezbędne do poprawnego odczytu zawartości pliku,
  • zapisywane są zawartości wszystkich komórek, także tytułowych - aby je wykluczyć należy inaczej indeksować dolne granice pętli,
  • zapis odbywa się do pliku amorficznego o formacie *.sgf (od StringG*rid file* - przykładowe rozszerzenie),
  • nie są obsługiwane wyjątki jeśli zapis/odczyt nie powiedzie się (jeśli nie można utworzyć pliku, nie można go otworzyć czy wystąpi błąd podczas odczytywania);
    Aby wyposażyć algorytm w ww. zabezpieczenia/unowocześnienia wystarczy poświęcić pół godziny - góra godzinę pod warunkiem, że w pełni rozumie się kod i dokładnie wie się co wykonuje;

Zapis zawartości StringGrid do pliku jest rzeczą bardzo prostą, należy po kolei:

1. zapisać ilość kolumn,
2. zapisać ilość wierszy,
3. w pętli (kolumny):
   1. w pętli (wiersze):
      1. pobrać długość łańcucha z komórki o współrzędnych pobranych z dwóch pętli,
      2. zapisać długość łańcucha,
      3. jeśli długość łańcucha jest większa niż 0:
         1. zapisać łańcuch;

Kod przykładowej procedury:

procedure SaveGridToFile(const AFileName: TFileName; AGrid: TStringGrid);
var
  intColCnt, intRowCnt, intCol, intRow: Word;
  intValueLen: Byte;
begin
  with TFileStream.Create(AFileName, fmCreate) do
  try
    intColCnt := AGrid.ColCount;
    intRowCnt := AGrid.RowCount;
    WriteBuffer(intColCnt, SizeOf(Word));
    WriteBuffer(intRowCnt, SizeOf(Word));

    for intCol := 0 to intColCnt - 1 do
      for intRow := 0 to intRowCnt - 1 do
      begin
        intValueLen := Length(AGrid.Cells[intCol, intRow]);
        WriteBuffer(intValueLen, SizeOf(Byte));

        if intValueLen > 0 then
          WriteBuffer(AGrid.Cells[intCol, intRow][1], intValueLen);
      end;
  finally
    Free();
  end;
end;

Procedura do zapisu wykorzystuje strumień klasy TFileStream;


Aby odczytać z pliku wcześniej zapisaną zawartość StringGrid należy po kolei:

1 wczytać ilość kolumn,
2. wczytać ilość wierszy,
3. ustawić w kontrolce ilość kolumn,
4. ustawić w kontrolce ilość wierszy,
5. w pętli (kolumny):
   1. w pętli (wiersze):
      1. wczytać długość łańcucha dla danej komórki,
      2. jeśli długość łańcucha jest większa niż 0:
         1. ustawić długość pomocniczej zmiennej łańcuchowej na tą wczytaną,
         2. wczytać łańcuch z pliku do zmiennej pomocniczej,
         3. ustawić wartość komórki kopiując (przypisując) łańcuch pomocniczy;

Kod przykładowej procedury:

procedure LoadGridFromFile(const AFileName: TFileName; AGrid: TStringGrid);
var
  intColCnt, intRowCnt, intCol, intRow: Word;
  intValueLen: Byte;
  ansiValue: AnsiString;
begin
  with TFileStream.Create(AFileName, fmOpenRead) do
  try
    Position := 0;  // ta linia nie jest potrzebna

    ReadBuffer(intColCnt, SizeOf(Word));
    ReadBuffer(intRowCnt, SizeOf(Word));
    AGrid.ColCount := intColCnt;
    AGrid.RowCount := intRowCnt;

    for intCol := 0 to intColCnt - 1 do
      for intRow := 0 to intRowCnt - 1 do
      begin
        ReadBuffer(intValueLen, SizeOf(Byte));

        if intValueLen > 0 then
        begin
          SetLength(ansiValue, intValueLen);
          ReadBuffer(ansiValue[1], intValueLen);
          AGrid.Cells[intCol, intRow] := ansiValue;
        end;
      end;
  finally
    Free();
  end;
end;

Podobnie jak do zapisu - procedura korzysta z klasy TFileStream;


Dodatkowo w załączniku zamieszczam aplikację (bez pliku wykonywalnego), w której testowałem ww. algorytmy - pisana pod Delphi7, jednak można ją bez problemu zaimportować do Lazarusa i tam sprawdzić jej działanie (ja nie testowałem).

0

Tle wystarczy, Cały kod umieszczę po skończeniu.

0
unit dziennikLekcyjnyGP;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, Menus, StdCtrls, ComCtrls, TabNotBk, Grids, ExtCtrls, Tabs, ExtDlgs,ShellAPI;

type
  TForm1 = class(TForm)
    MainMenu: TMainMenu;
    Plik1: TMenuItem;
    Koniec2: TMenuItem;
    Zapisz1: TMenuItem;
    Wczytaj1: TMenuItem;
    Obecnosc1: TMenuItem;
    Srednia1: TMenuItem;
    PageControl1: TPageControl;
    Dodajzak1: TMenuItem;
    Zamknijzak1: TMenuItem;
    Edit1: TEdit;
    procedure Koniec2Click(Sender: TObject);
    procedure Wczytaj1Click(Sender: TObject);
    procedure Zapisz1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure Dodajzak1Click(Sender: TObject);
    procedure Zamknijzak1Click(Sender: TObject);
    procedure Srednia1Click(Sender: TObject);
    procedure Obecnosc1Click(Sender: TObject);
  private
    { Private declarations }
  public
  { Public declarations }
    i,k,tab:integer;
    plik: file;
    dane:string;
  end;

var
  Form1: TForm1;
  StringGrid:TStringGrid;


implementation
  {$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);  //jest uzywana na onCreat i po nacisknieci nowy
begin                                          //wywoluje procedure wypelnij_tabele
  tab:=0;
  i:=2;
  k:=3;
end;

procedure wypelnij_tabele(var s:TstringGrid); //przypisuje nazwy do komurek
var i,j:integer;
begin
  with s do begin
  Cells[1,0]:='Imie';
  Cells[0,1]:='Daty lekcji';
  Cells[2,0]:='Nazwisko';
  Cells[3,0]:='Oceny';
  Cells[12,0]:='Średnia';
  Cells[13,0]:='Obecnosc';
  Cells[33,0]:='Uwagi';
  Cells[41,0]:='Komentarze';
  begin
    for i:=2 to 52 do
      begin
      for j:=13 to 32 do
        begin
          Cells[j,i]:='Obecny';
        end;
        cells[0,i]:=IntToStr(i);
      end;
  end;
  for i:=1 to 52 do
    cells[0,i+1]:=IntToStr(i);
  end;
end;

//zapis tabeli do pliku
procedure SaveStringGrid(StringGrid: TStringGrid; const FileName: TFileName);
var
  f:    TextFile;
  i, k: Integer;
begin
  AssignFile(f, FileName);
  Rewrite(f);
  with StringGrid do
  begin
    Writeln(f, ColCount); // zapisuje liczbe kolumn i wierszy
    Writeln(f, RowCount);
    for i := 0 to ColCount - 1 do    // zapisuje wszystkie pomurki stinggrida
      for k := 0 to RowCount - 1 do
        Writeln(f, Cells[i, k]);
  end;
  CloseFile(f);
end;

//odczyt tabeli z pliku
procedure LoadStringGrid(StringGrid: TStringGrid; const FileName: TFileName);
var
  f:          TextFile;
  iTmp, i, k: Integer;
  strTemp:    String;
begin
  AssignFile(f, FileName);
  Reset(f);
  with StringGrid do
  begin
    Readln(f, iTmp);       // pobiera liczbe kolumn
    ColCount := iTmp;
    Readln(f, iTmp);      // pobiera liczbe wierszy
    RowCount := iTmp;
    for i := 0 to ColCount - 1 do      // robi petle i wylełnia komurki
      for k := 0 to RowCount - 1 do
      begin
        Readln(f, strTemp);
        Cells[i, k] := strTemp;
      end;
  end;
  CloseFile(f);
end;

procedure TForm1.Dodajzak1Click(Sender: TObject); //Dodaje zakladke
var
  TabSheet: TTabSheet;
  nazwa:string;

begin                  //Okienko z pyt o nazwe zakłądki i tworzy stringgrid w pagecontrol
  Edit1.Text:=InputBox('Dodawanie przedmiotu','Podaj nazwe przedmiotu:','Tu wpisz nazwe przedmiotu');
  if (Edit1.Text<>'') and (Edit1.Text<>'Tu wpisz nazwe przedmiotu') then
  begin
  nazwa:=(Edit1.Text);
  TabSheet := TTabSheet.Create(PageControl1);
  TabSheet.Caption := nazwa;
  TabSheet.PageControl := PageControl1;
  StringGrid := TStringGrid.Create (TabSheet);
  StringGrid.ColCount := 50;
  StringGrid.Align:=alClient;
  StringGrid.ScrollBars:=ssBoth;
  StringGrid.Options := StringGrid.Options + [goThumbTracking];
  StringGrid.Options:= StringGrid.Options + [goEditing];
  StringGrid.Options:= StringGrid.Options + [goFixedRowClick];
  StringGrid.Options:= StringGrid.Options + [goFixedColClick];
  StringGrid.Options:= StringGrid.Options + [goTabs];
  StringGrid.Options:= StringGrid.Options + [goRowSizing];
  StringGrid.Options:= StringGrid.Options + [goColSizing];
  StringGrid.Parent := TabSheet;
  wypelnij_tabele(StringGrid);
  StringGrid.RowCount := 52;
  StringGrid.Visible := True;
  tab:=tab+1;
  end;
end;

procedure TForm1.Koniec2Click(Sender: TObject);  //zamyka program
begin
  Close;
end;

procedure TForm1.Obecnosc1Click(Sender: TObject);  //wpisuje nieobennosc do komurki z wybramym nazwiskiem
var
    nazwisko,data: String;
    col,row:integer;
begin
             begin
                Edit1.Text:=InputBox('Nie obecni','Podaj nazwisko nieobecnej osoby:','Nazwisko');
                nazwisko:=(Edit1.Text);
             end;
             begin
                Edit1.Text:=InputBox('Nie obecni','Podaj date nieobecnosci:','Data');
                data:=(Edit1.Text);
             end;
              for row := 1 to 52 do
              for col := 13 to 32 do

              begin
                if (PageControl1.ActivePage.Controls[0] as TStringGrid).Cells[col,1]=data then
                if (PageControl1.ActivePage.Controls[0] as TStringGrid).Cells[2,row]=nazwisko then
                  begin
                    (PageControl1.ActivePage.Controls[0] as TStringGrid).Row:=row;//zaznaczanie komorki w danym wierszy
                    (PageControl1.ActivePage.Controls[0] as TStringGrid).Col:=col;//zaznaczanie komorki w danej kolumnie
                    (PageControl1.ActivePage.Controls[0] as TStringGrid).Cells[col,row]:=('Nie obecny');
                  end;
              end;

           end;

procedure TForm1.Srednia1Click(Sender: TObject); //oblicza srednia ocen
var
  col,row,dziel,suma:integer;
  sr:real;
begin
  for row := 1 to StringGrid.RowCount do
      begin
         suma:=0;
         dziel:=0;
         sr:=0;
         for col := 3 to 10 do
           begin
             if (PageControl1.ActivePage.Controls[0] as TStringGrid).Cells[col,row]='' then
             else
             begin
                suma:=suma+StrToInt((PageControl1.ActivePage.Controls[0] as TStringGrid).Cells[col,row]);
                dziel:=dziel+1;
             end;
           end;
           if (dziel>0) then
             begin
                sr:=suma / dziel;
                (PageControl1.ActivePage.Controls[0] as TStringGrid).Cells[12,row]:=FloatToStr((Round(sr*100)/100));
             end;
        end;
end;


procedure TForm1.Wczytaj1Click(Sender: TObject);   //wczytuje stringgrid po kolumn i wierszy
var
q:integer;
TabSheet: TTabSheet;
nazwa:string;
w:TextFile;

begin
  AssignFile(w, '.\zapis\dane.txt');
  reset(w);
  readln(w,tab);
  for q := 0 to tab -1 do
  begin
      readln(w,nazwa);
      TabSheet := TTabSheet.Create(PageControl1);
      TabSheet.Caption := nazwa;
      TabSheet.PageControl := PageControl1;
      StringGrid := TStringGrid.Create (TabSheet);
      StringGrid.ColCount := 50;
      StringGrid.Align:=alClient;
      StringGrid.ScrollBars:=ssBoth;
      StringGrid.Options := StringGrid.Options + [goThumbTracking];
      StringGrid.Options:= StringGrid.Options + [goEditing];
      StringGrid.Options:= StringGrid.Options + [goFixedRowClick];
      StringGrid.Options:= StringGrid.Options + [goFixedColClick];
      StringGrid.Options:= StringGrid.Options + [goTabs];
      StringGrid.Options:= StringGrid.Options + [goRowSizing];
      StringGrid.Options:= StringGrid.Options + [goColSizing];
      StringGrid.Parent := TabSheet;
      StringGrid.RowCount := 52;
      wypelnij_tabele(StringGrid);
      StringGrid.Visible := True;
      PageControl1.ActivePageIndex:=q;
      LoadStringGrid((PageControl1.ActivePage.Controls[0] as TStringGrid), '.\zapis\'+nazwa+'.txt');
  end;
  CloseFile(w);
  end;

procedure TForm1.Zamknijzak1Click(Sender: TObject); //usuwa zakłądke akutalnie aktywną
begin
if FileExists('.\zapis\'+PageControl1.Pages[PageControl1.ActivePageIndex].Caption+'.txt') then
  begin
    PageControl1.Pages[PageControl1.ActivePageIndex].Free;
    tab:=tab-1;
  end
else
  begin
    PageControl1.Pages[PageControl1.ActivePageIndex].Free;
    tab:=tab-1;
  end;
end;

procedure TForm1.Zapisz1Click(Sender: TObject);   //zapisuje tekst z stringgrid to kol i wirszy
var
q:integer;
w:    TextFile;
begin
  AssignFile(w, '.\zapis\dane.txt');
  Rewrite(w);
  writeln(w,tab);
  for q := 0 to tab - 1 do
  begin
    writeln(w,PageControl1.Pages[q].Caption);
  end;
  CloseFile(w);

  for q := 0 to tab - 1 do
    begin
      PageControl1.ActivePageIndex:=q;
      SaveStringGrid((PageControl1.ActivePage.Controls[0] as TStringGrid), '.\zapis\'+PageControl1.Pages[q].Caption+'.txt');
    end;
end;

end.

dodanie znacznika <code class="delphi"> - fp

0

I co w związku z tym kodem? Chciałeś się tylko pochwalić, czy masz jeszcze jakies pytania?

Widzę, że skorzystałeś z najłatwiejszej opcji jaką jest zapis StringGrid do pliku tekstowego - nie jest to najlepsze rozwiązanie, bo łatwo taki plik zepsuć i program nie udławi; Jeśli takie rozwiązanie Cię satysfakcjonuje to nie ma sprawy, niemniej jednak polecam wykorzystać pliki amorficzne, nie dając tym samym takiej łatwości modyfikacji;

Poza tym masz sporo zbędnego kodu, np.:

  StringGrid.Options:= StringGrid.Options + [goThumbTracking];
  StringGrid.Options:= StringGrid.Options + [goEditing];
  StringGrid.Options:= StringGrid.Options + [goFixedRowClick];
  StringGrid.Options:= StringGrid.Options + [goFixedColClick];
  StringGrid.Options:= StringGrid.Options + [goTabs];
  StringGrid.Options:= StringGrid.Options + [goRowSizing];
  StringGrid.Options:= StringGrid.Options + [goColSizing];

możesz przecież napisać tak:

StringGrid.Options := [goThumbTracking, goEditing, goFixedRowClick, goFixedColClick,
                       goTabs, goRowSizing, goColSizing];

Dodatkowo trzeba zauważyć, że ten kod wykorzystujesz w dwóch miejscach, więc możesz zadeklarować sobie stałą ze zbiorem tych wartości i wykorzystywać ją wielokrotnie:

const
  SG_OPTIONS_SET: TGridOptions = [goThumbTracking, goEditing, goFixedRowClick, goFixedColClick, 
                                  goTabs, goRowSizing, goColSizing];

i potem wykorzystać go w ten sposób:

StringGrid.Options := SG_OPTIONS_SET

BTW: Skąd wziąłeś wartości goFixedRowClick i goFixedColClick? Typ TGridOption ani w Delphi7, ani w Lazarusie nie posiada takich wartości;

EDIT: już wiem skąd - dokumentacja XE2 rozwiała moje wątpliwości :]


Następnie ten kod:

begin
  if (PageControl1.ActivePage.Controls[0] as TStringGrid).Cells[col,1]=data then
    if (PageControl1.ActivePage.Controls[0] as TStringGrid).Cells[2,row]=nazwisko then
    begin
      (PageControl1.ActivePage.Controls[0] as TStringGrid).Row:=row;//zaznaczanie komorki w danym wierszy
      (PageControl1.ActivePage.Controls[0] as TStringGrid).Col:=col;//zaznaczanie komorki w danej kolumnie
      (PageControl1.ActivePage.Controls[0] as TStringGrid).Cells[col,row]:=('Nie obecny');
    end;
end;

Można skrócić i uprościć, zamiast wielokrotnie rzutować operatorem As:

var
  sgActive: TStringGrid;
  {...}
begin
  {...}

  begin
    sgActive := (PageControl1.ActivePage.Controls[0] as TStringGrid);

    if (sgActive.Cells[Col, 1] = Data) and (sgActive.Cells[2, Row] = Nazwisko) then
    begin
      sgActive.Row := Row;
      sgActive.Col := Col;
      sgActive.Cells[Col, Row] := 'Nieobecny';
    end;
  end;

  {...}

Z racji tej, że sgActive nie tworzysz, bo przypisujesz do niej referencję istniejącego komponentu - także go nie zwalniasz; Przez to kod staje się uproszczony i wydajniejszy, bo rzutowanie przez As wykonujesz raz, a później odwołujesz się już do obiektu bezpośrednio, nie przez kolejne kilka rzutowań; Poza tym wykorzystujesz zmienne o identyfikatorach takich samych, jak nazwy właściwości komponentu, więc w kodzie wyżej nie można zastosować instrukcji wiążącej With - postaraj się lepiej nazywać zmienne/stałe;

Ja wykorzystuję znienawidzoną przez większość programistów notację węgierską, przez co mogę zmienne nazywać tak samo jak właściwości klas czy pola rekordów i mogę wykorzystać do ich modyfikacji/odczytywania instrukcję wiążącą; Ale to tylko luźna sugestia;


I na koniec:

procedure TForm1.Zamknijzak1Click(Sender: TObject); //usuwa zakłądke akutalnie aktywną
begin
if FileExists('.\zapis\'+PageControl1.Pages[PageControl1.ActivePageIndex].Caption+'.txt') then
  begin
    PageControl1.Pages[PageControl1.ActivePageIndex].Free;
    tab:=tab-1;
  end
else
  begin
    PageControl1.Pages[PageControl1.ActivePageIndex].Free;
    tab:=tab-1;
  end;
end;

Kod tego zdarzenia nie ma żadnego sensu - bez względu na stan warunku zostaną wykonane dokładnie te same instrukcje, a funkcja FileExists nie ma tutaj żadnego zastosowania, bo całość można skrócić do:

procedure TForm1.Zamknijzak1Click(Sender: TObject);
begin
  PageControl1.Pages[PageControl1.ActivePageIndex].Free;
  Dec(Tab);
end;

i będzie robić dokładnie to samo;


Tak więc przysiądź jeszcze trochę nad tym kodem i go zoptymalizuj, bo można go znacząco skrócić.

0

Chciałem się podzielić.

1 użytkowników online, w tym zalogowanych: 0, gości: 1