Kolorowanie wybranej całej kolumny w StringGrid po obliczeniu jej indeksu

0

Witam.

Mam problem z kolorowaniem pozycji w StringGrid. Żadne z tematów które napotkałem mi nie pomagają bo jestem zbyt zielony żeby wprowadzić w życie to co jest tam napisane. Przejdźmy jednak do rzeczy. Mam powiedzmy macierz 10x10 wypełnioną losowymi cyframi. Muszę podświetlić kolumnę o najmniejszej średniej i zupełnie nie mam pojęcia jak to zrobić.

     if CheckBox4.Checked = TRUE then
     begin
       srednia:=10;
       suma:=0;
       for i:=1 to n do
       begin
         suma:=0;
       for j:=1 to n do
           begin
             macierz[i,j]:=strtoint(StringGrid1.Cells[i,j]);
             suma:=suma+macierz[i,j];
           end;
           if (suma/n)<srednia then
           begin
           srednia:=(suma/n);
           kolumna:=i;
           end;
       end;
       if StaticText1.Caption = '' then StaticText1.Caption:= 'Kolumna o najmniejszej sredniej to: ' + inttostr(kolumna)
       else StaticText1.Caption:=StaticText1.Caption + #10 + 'Kolumna o najmniejszej sredniej to: ' + inttostr(kolumna);
     end;

Oto kod części aplikacji. Mam nadzieję, że jest czytelny i zrozumiały.

Za wszelką pomoc dziękuję.

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

1

Ja bym te dane trzymał w tablicy statycznej lub dynamicznej, w zależności od potrzeb i tylko wyświetlał w StringGridzie, czyli:

  1. Deklaracja tablicy np:
    var 
    a: array[0..9] of array[0..9] of Integer;
  2. Wypełnienie tablicy danymi.
  3. znalezienie w tablicy interesującej Cię kolumny i przypisanie wartości do zmiennej globalnej.
  4. Wypełnienie StringGrida.
  5. Malowanie StringGrida (Object Inspektor -> Events -> OnDrawCell), poniżej przykład, jedynkę należy zastąpić zmienną globalną.
procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
  Rect: TRect; State: TGridDrawState);
begin
  if (ACol = 1) then
  begin
    TStringGrid(Sender).Canvas.Brush.Color := Clred;
    TStringGrid(Sender).Canvas.FillRect(Rect);
    TStringGrid(Sender).Canvas.TextRect(Rect, Rect.Left + 2, Rect.Top + 2, TStringGrid(Sender).Cells[aCol, aRow]);
  end;
end; 
1

Muszę podświetlić kolumnę o najmniejszej średniej i zupełnie nie mam pojęcia jak to zrobić.

Jeśli chodzi o samo "podświetlenie" to najprostszym sposobem jest wykorzystanie właściwości Selection:

procedure TForm1.Button1Click(Sender: TObject);
const
  COLUMN_ID = 2;
begin
  with StringGrid1 do
    Selection := TGridRect(Rect(COLUMN_ID, FixedRows, COLUMN_ID, RowCount));
end;

gdzie COLUMN_ID to indeks kolumny do podświetlenia; Powyższy kod pomija komórkę (lub komórki) nagłówka kolumny, więc jest uniwersalny dla dowolnie ustawionej właściwości FixedRows;

Nie napisałeś czy potrzebujesz to zaznaczenie mieć na chwilę czy na dłużej, więc nie wiem czy chcesz ręcznie malować komórki, czy wykorzystać standardowe zaznaczenie;

janeks napisał(a)
  1. znalezienie w tablicy interesującej Cię kolumny i przypisanie wartości do zmiennej globalnej.

Taa, najlepiej to wszędzie pakować zmienne globalne a potem narzekać, że ciężko się w kodzie połapać i że "jakimś dziwnym trafem mam widoczne wszystkie zmienne w projekcie - co się dzieje?" o.O

0

Dziękuję wam bardzo. Wszystko będzie w aplikacji i muszę mieć opcję wskazania kolumny o najmniejszej średniej więc ten drugi sposób chyba się nie sprawdzi. Zmienną globalną co do kolumny i tak mam, jak na razie tylko w ten sposób sobie z tym poradziłem. Co do pierwszej odpowiedzi mam pytanie. Czy wykonanie wszystkich czynności i wrzucenie tej procedury do kodu wystarczy? Dokładniej pytam o to czy w jakiś sposób muszę tą procedurę wywołać, bo jeśli tak to chyba do końca nie wiem jak.

Bardzo dziękuję za odpowiedzi.

1
BoB0_ napisał(a):

Co do pierwszej odpowiedzi mam pytanie. Czy wykonanie wszystkich czynności i wrzucenie tej procedury do kodu wystarczy? Dokładniej pytam o to czy w jakiś sposób muszę tą procedurę wywołać, bo jeśli tak to chyba do końca nie wiem jak.

  1. Klikasz na StringGrida (zostanie zaznaczony),
  2. W Object Inspector klikasz na zakładkę Events,
  3. Robisz "dwuklik" na pustym polu obok OnDrawCell,
  4. Modyfikujesz stworzoną przez środowisko procedurę, ma być taka jak ta z wcześniejszego mojego postu.
    Ona działa automatycznie w chwili odmalowywania przez system, program StringGrida.
    Aby ręcznie wykonać w kodzie odmalowywanie możesz użyć StringGrid1.Repaint;
1

@BoB0_ - dostałeś dwie propozycje, jedna (kolegi @janeks) dotycząca ręcznego malowania komórek komponentu i druga (moja) dotycząca wykorzystania właściwości Selection do podświetlenia komórek wybranej kolumny; Zdecyduj którą wersję chcesz i zaimplementuj ją u siebie w kodzie;

Obie mają swoje plusy i minusy (zależne od potrzeb), bo ręczne malowanie komórek wymaga oprogramowania zdarzenia do tego celu przeznaczonego i dbanie o poprawne przerysowywanie komponentu w odpowiednich miejscach w kodzie, ale za to podświetlenie nie zniknie po zaznaczeniu komórki z innej kolumny (będzie stałe, aż do ponownego przerysowania komponentu po zmodyfikowaniu odpowiednich danych, z których zdarzenie korzysta);

Z kolei mój sposób zwalnia Cię z oprogramowywania zdarzenia OnDrawCell dzięki wykorzystaniu gotowej właściwości i wbudowanemu podświetlaniu danych komórek, ale podświetlenie to będzie widoczne do czasu zaznaczenia komórki z innej kolumny (do pierwszego kliknięcia na komponent);

Masz dwie opcje, ale wszystko zależy od Ciebie i sposobu zaznaczania kolumny; Jeśli podświetlenie ma być widoczne dłużej (nie znikać przy kolejnych kliknięciach) to wybierz sposób @janeks, a jeśli ma być tylko chwilowe (i zniknąć przy kolejnym kliknięciu aby umożliwić dalszą modyfikację danych zawartych w komponencie) to skorzystaj z mojej sugestii;


BoB0_ napisał(a)

Zmienną globalną co do kolumny i tak mam, jak na razie tylko w ten sposób sobie z tym poradziłem.

Zmienne globalne to zło - po co masz ją uwidoczniać w całym programie, skoro i tak tylko jeden komponent z niej korzysta? Stwórz prywatne pole w klasie choćby formularza i je wykorzystaj (skoro i tak pewnie nie masz jakiejś klasy zarządzającej danymi komponentu);

BoB0_ napisał(a)

Czy wykonanie wszystkich czynności i wrzucenie tej procedury do kodu wystarczy?

Jeśli masz na myśli mój sposób to tak - wystarczy wrzucić podaną przeze mnie instrukcję do Twojego kodu i będzie działać; Musisz jedynie zmienić nazwę komponentu (jeśli się różni od tej podanej) i w miejsce stałej COLUMN_ID wstawić zmienną, w której przechowujesz indeks kolumny po obliczeniach; Nie musisz dodatkowo przerysowywać komponentu, bo modyfikacja właściwości Selection automatycznie podświetli wybrane komórki;

Jeśli chodzi o drugi sposób z ręcznym malowaniem to nie - nie wystarczy kodu przekleić, trzeba go napisać samemu i dostosować pod swoje wymagania; Dzięki ręcznemu rysowaniu komórek masz dużo większe możliwości dostosowania wyglądu komponentu pod swoje wymagania, bo możesz je narysować tak, jak chcesz (nawet pododawać obrazki); Wiąże się to oczywiście z napisaniem dużo większej ilości kodu i zabezpieczenie go przed błędnym działaniem - potrzeba testów i sprawdzenia wszystkich przypadków, aby komórki zawsze były rysowane poprawnie;

Dokładniej pytam o to czy w jakiś sposób muszę tą procedurę wywołać, bo jeśli tak to chyba do końca nie wiem jak.

Jeżeli wybierzesz mój sposób z wykorzystaniem do tego celu właściwości Selection wystarczy ją ustawić, a komponent zostanie automatycznie przerysowany; Jeśli chcesz ręcznie rysować komórki to musisz wywołać zdarzenie, które przemaluje cały komponent i narysuje inaczej podświetlone komórki - czyli wywołać w odpowiednim miejscu metodę Repaint komponentu.

0

Na początku chciałbym wam podziękować za odpowiedź, szczególnie tobie Furious za tak obszerną wersję. Najbardziej odpowiednią opcją wydaje się być Repaint , problem w tym że zupełnie nie wiem od czego zacząć i jak się do tego zabrać. W związku z tym załączę tutaj kod swojej aplikacji i po krótce postaram się wam wytłumaczyć o co mi dokładniej chodzi. Wasza ingerencja w mój kod znacznie mi pomoże.

   public
    n, kolumna:integer;
    macierz: array [1..rM,1..rM] of Integer;
    { public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.lfm}

{ TForm1 }

procedure TForm1.FormCreate(Sender: TObject);
begin
  StringGrid1.ColCount := 0;
  StringGrid1.RowCount := 0;
end;

procedure TForm1.Button1Click(Sender: TObject);
var i,j:integer;
begin
  n:= StrToInt(ComboBox1.Text);
  StringGrid1.ColCount:=n+1;
  StringGrid1.RowCount:=n+1;
  Label2.Caption:='Twoja macierz: ';
  for i:=1 to n do
      StringGrid1.Cells[0,i]:=IntToStr(i);
  for i:=1 to n do
      StringGrid1.Cells[i,0] := IntToStr(i);
  Randomize;
  for i:=1 to n do
        for j:=1 to n do
            macierz[i,j]:= random(11)-0;
    for i:=1 to n do
        for j:=1 to n do
            StringGrid1.Cells[i,j] := IntToStr(macierz[i,j]);
    StaticText1.Caption := '';
    CheckBox1.Checked := FALSE;
    CheckBox2.Checked := FALSE;
    CheckBox3.Checked := FALSE;
    CheckBox4.Checked := FALSE;
    CheckBox6.Checked := FALSE;
end;

procedure TForm1.Button2Click(Sender: TObject);
var i,j, s :integer;
  suma :integer;
  srednia:double;
  trans: array [1..rM,1..rm] of integer;
begin
  if StringGrid1.ColCount > 0 then
  begin
  StaticText1.Caption := '';
     if CheckBox1.Checked = TRUE then
     begin
       suma:=0;
       for i:=1 to n do
       for j:=1 to n do
           begin
             macierz[i,j]:=strtoint(StringGrid1.Cells[i,j]);
             suma:=suma+macierz[i,j];
           end;
       StaticText1.Caption :='Suma wszystkich elementow to: ' + inttostr(suma);
     end;
     if CheckBox2.Checked = TRUE then
     begin
       suma:=0;
       for i:=1 to n do
      begin
        j:=i;
        macierz[i,j]:=strtoint(StringGrid1.Cells[i,j]);
        suma:=suma+macierz[i,j];
      end;
       if StaticText1.Caption = '' then StaticText1.Caption:= 'Suma wszystkich elementow glownej przekatnej to: ' + inttostr(suma)
       else StaticText1.Caption:=StaticText1.Caption + #10 + 'Suma wszystkich elementow glownej przekatnej to: ' + inttostr(suma);
     end;
     if CheckBox3.Checked = TRUE then
     begin
       suma:=0;
       s:=0;
       for i:=1 to n do
       begin
         j:=i;
         macierz[i,j]:=strtoint(StringGrid1.Cells[i,j]);
         suma:=suma+macierz[i,j];
         s:=s+1;
       end;
       if StaticText1.Caption = '' then StaticText1.Caption:=  'Srednia glownej przekatnej to: ' + floattostr(suma/s)
       else StaticText1.Caption := StaticText1.Caption + #10 + 'Srednia glownej przekatnej to: ' + floattostr(suma/s);
     end;
     if CheckBox4.Checked = TRUE then
     begin
       srednia:=10;
       suma:=0;
       for i:=1 to n do
       begin
         suma:=0;
       for j:=1 to n do
           begin
             macierz[i,j]:=strtoint(StringGrid1.Cells[i,j]);
             suma:=suma+macierz[i,j];
           end;
           if (suma/n)<srednia then
           begin
           srednia:=(suma/n);
           kolumna:=i;

           end;
       end;
       if StaticText1.Caption = '' then StaticText1.Caption:= 'Kolumna o najmniejszej sredniej to: ' + inttostr(kolumna)
       else StaticText1.Caption:=StaticText1.Caption + #10 + 'Kolumna o najmniejszej sredniej to: ' + inttostr(kolumna);
     end;
     if CheckBox5.Checked = TRUE then
     begin
       for i:=1 to  n do
       for j:=1 to n do
           begin
                macierz[i,j]:=strtoint(StringGrid1.Cells[i,j]);
           end;
           for i:=1 to n do
               for j:=1 to n do
                   begin
                   trans[i,j]:=macierz[j,i];
                   StringGrid1.Cells[i,j]:=IntToStr(trans[i,j]);
                 end;
      Label2.Caption:='Transponowana macierz to: ';
     end;
     if CheckBox6.Checked = TRUE then
     begin
     end;
  end
  else StaticText1.Caption := 'STWORZ MACIERZ!';
end;

procedure TForm1.Button3Click(Sender: TObject);
var i,j:integer;
    trans: array [1..rM,1..rm] of integer;
begin
  if StringGrid1.ColCount > 0 then
  begin
  for i:=1 to  n do
       for j:=1 to n do
           begin
                macierz[i,j]:=strtoint(StringGrid1.Cells[i,j]);
           end;
   for i:=1 to n do
       for j:=1 to n do
           begin
                trans[i,j]:=macierz[j,i];
                StringGrid1.Cells[i,j]:=IntToStr(trans[i,j]);
           end;
      Label2.Caption:='Transponowana macierz to: ';
      CheckBox1.Checked := FALSE;
      CheckBox2.Checked := FALSE;
      CheckBox3.Checked := FALSE;
      CheckBox4.Checked := FALSE;
      CheckBox6.Checked := FALSE;
      StaticText1.Caption := #10 + #10 + #10 + 'MACIERZ ZOSTALA TRANSPONOWANA';
  end
  else StaticText1.Caption := 'STWORZ MACIERZ!';
end;

procedure TForm1.Button4Click(Sender: TObject);
begin
  Close;
end;

end.

W aplikacji mam stringgrid, którego nie modyfikuję. Wyświetlane są w nim tylko jakieś cyfry lub liczby z pewnego zakresu. Ma to na celu przedstawić generowaną macierz o dowolnie wybranym wymiarze. Po zaznaczeniu CheckBox4 i wciśnięciu przycisku Button2 ma znaleźć kolumnę z najmniejszą średnią i ją pokolorować na jakiś kolorek. Ma tak pozostać do kolejnego generowania macierzy. Cały czas problem w tym że nie wiem jak to pokolorować mimo iż podaliście mi dwie możliwości. Z tą od Furious sobie poradziłem bo wymagała tylko wklejenia kodu w odpowiednie miejsce ale nie o to mi chodziło. Tak jak wspomniałem chciałbym użyć Repaint ale nie wiem jak to zrobić. Byłbym wdzięczny gdybyście mi w jakikolwiek pomogli. Mam również nadzieję, że moja desperacja was nie zniechęci.

Pozdrawiam i dziękuję.

usunięcie znacznika <spoiler> - fp

1

Tak jak wspomniałem chciałbym użyć Repaint ale nie wiem jak to zrobić.

Musisz ustawić pole goEditing we właściwości Options na False, po czym oprogramować zdarzenie OnDrawCell; Jeżeli nie wiesz jak się rysuje po Canvas to poszukaj w sieci informacji - jest tego pełno;

Poniżej przykładowe zdarzenie rysujące komórkę:

procedure TMainForm.sgViewDrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState);
var
  Grid: TStringGrid;
begin
  Grid := TStringGrid(Sender);

  with Grid.Canvas do
  begin
    // jeśli komórka jest zaznaczona
    if gdSelected in State then
    begin
      Pen.Color := clGray;
      Brush.Color := clWhite;
      Font.Color := clBlack;
    end
    else
      // jeśli komórka ma być malowana innym kolorem
      if ACol = seColumnID.Value then
      begin
        Pen.Color := $0101F1;
        Brush.Color := $053EFE;
        Font.Color := clWhite;
      end
      else
      // zwykła komórka - nie zaznaczona i w innej kolumnie, niż ta obliczona
      begin
        Pen.Color := $BBBBBB;
        Brush.Color := $DDDDDD;
        Font.Color := clGray;
      end;

    Rectangle(Rect);
    TextOut(Rect.Left + 3, Rect.Top + 3, Grid.Cells[ACol, ARow]);
  end;
end;

Jak widać rozróżniany jest stan komórki w warunku (zaznaczona lub nie) i jej indeks - jeśli jest zaznaczona, będzie malowana na biało z szarą obwódką; Jeśli nie jest zaznaczona sprawdzane jest, czy znajduje się w kolumnie, która ma być innym kolorem i jeśli tak - malowana jest na czerwono, a jeśli nie - na szaro; Kolor tekstu także jest ustalany, aby był inny dla kazdego stanu komórki; Poniżej efekt działania kodu:

sgdrawing.png

U Ciebie będziesz sobie musisał zmienić warunek if ACol = seColumnID.Value then na sprawdzający z wynikiem obliczeń, czyli jak dobrze rozumiem if ACol = Kolumna then; Pod warunkiem, że pole Kolumna ma przechowywać indeks kolumny do malowania na inny kolor; Jeśli chciałbyś włączać lub wyłączać osobne kolorowanie kolumny to w zdarzeniu OnDrawCell powinieneś wykonać to w warunku - jeśli ma być kolorowane przypisujesz inne kolory właściwościom Pen, Brush i Font, a jeśli nie to przypisujesz takie, jak przy normalnym malowaniu zwyłej - nie zaznaczonej komórki;

Po obliczeniach i ustaleniu indeksu kolumny do przemalowania na inny kolor musisz wywołać metodę Repaint komponentu, aby zmiany zostały wprowadzone; W moim przykładowym programie jest to wykonywane po zmianie wartości SpinEdit (w zdarzeniu OnChange) - Ty sobie ustal kiedy ma nastąpić i odpowiednio dodaj wywołanie tej metody;

No i jeszcze jedna uwaga - jeśli posiadasz komórki nagłówków (FixedCols > 0 lub FixedRows > 0) to także musisz to sprawdzić zanim ustalisz kolory komórki; Sprawdzasz parametry ACol i ARow zdarzenia OnDrawCell czy są większe lub równe FixedCols i FixedRows i jeśli tak - ustalasz kolory dla komórki nagłówka; Jeśli nie - ustalasz tak jakw powyższym kodzie rozróżniając różne przypadki;

Mam nadzieję, że teraz już wszystko jest jasne; Do posta dołączam projekt (bez pliku wykonywalnego) z powyższego zrzutu - pobaw się nią i spróbuj rysować komórki po swojemu.

0

Wyrzuca mi taki błąd przy kompilacji:

unit1.pas(77,13) Error: Wrong number of parameters specified for call to "Rect"
Ten sam błąd jest w tych liniach:

Rectangle(Rect);
    TextOut(Rect.Left + 3, Rect.Top + 3, Grid.Cells[ACol, ARow]);

edit.

Poprawione. Teraz mam coś takiego:

unit1.pas(78,14) Error: Identifier not found "Rectangle"
unit1.pas(79,12) Error: Identifier not found "TextOut"
unit1.pas(233) Fatal: There were 2 errors compiling module, stopping

edit2.

To również poprawione.

0

A jak mogę pokolorować wszystko? Zrobiłem coś takiego:

procedure TForm1.StringGrid1DrawCell(Sender: TObject; aCol, aRow: Integer;
  Rect: TRect; aState: TGridDrawState);
var
  Grid: TStringGrid;
begin
  Grid := TStringGrid(Sender);

  with Grid.Canvas do
  begin
       if czygeneruj = TRUE then
       begin
        Pen.Color := $BBBBBB;
        Brush.Color := $DDDDDD;
        Font.Color := clGray;
      end;
      if czywykonaj = TRUE then
      begin
      if ACol = kolumna then
      begin
        Pen.Color := $0101F1;
        Brush.Color := $053EFE;
        Font.Color := clWhite;
      end;
      end;

    Rectangle(Rect);
    TextOut(Rect.Left + 3, Rect.Top + 3, Grid.Cells[ACol, ARow]);
  end;
czygeneruj:=FALSE;
end;

I jakoś mniej więcej działa, ale jest problem. Otóż jak mi pokoloruje tą kolumnę o najmniejszej średniej i wygeneruje nową macierz to mi zostaje kolor.

Już ten problem usunąłem.

Mam jednak kolejne pytanie. W jaki sposób mogę usunąć numerowanie wierszy i kolumn tak jak to jest pokazane w przykładzie wyżej?

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