Помощь - Поиск - Пользователи - Календарь
Полная версия: и опять TStringGrid
Форум «Всё о Паскале» > Современный Паскаль и другие языки > Делфи
marwell
Доброго времени суток
Само задание звучит так: Дана таблица целых чисел ai, bi. Выделить цветом все совпадающие пары и максимальную из них, указать позиции.
Нашел в инете как выделять цветом только отдельные ячейки, но не уверен что правильно ее применяю. Еще выдает ошибку "Project Projectl.exe raised exception class EConvertError with message'" Is not a valid integer value1. Process stopped. Use Step or Run to continue." Смысл ошибки понимаю, но не вижу в каком месте она появляется, вроде везде правильно работаю с ячейками

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Grids;

type
TForm1 = class(TForm)
SG: TStringGrid;
Edit1: TEdit;
procedure FormPaint(Sender: TObject);
procedure Edit1Change(Sender: TObject);
procedure SGDrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect;
State: TGridDrawState);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormPaint(Sender: TObject);
var i:integer;
begin
with SG do begin
cells[0,0]:='N';
cells[0,1]:='ai';
cells[0,2]:='bi';
for i:=1 to ColCount do
cells[i,0]:=IntToStr(i);
end;
end;

procedure TForm1.Edit1Change(Sender: TObject);
var i:integer;
begin
SG.ColCount := StrToInt((Sender as TEdit).Text);
for i:=1 to SG.ColCount do
SG.cells[i,0]:=IntToStr(i);
end;

procedure TForm1.SGDrawCell(Sender: TObject; ACol, ARow: Integer;
Rect: TRect; State: TGridDrawState);
var i,j,max:integer;
begin
max:=0;
j:=0;
for i:=1 to sg.ColCount-1 do begin
if StrToInt(sg.Cells[i,j+1])=StrToInt(sg.Cells[i,j+2]) then sg.Canvas.Brush.Color:=clRed;
if StrToInt(sg.Cells[i,j])>max then
begin max:=StrToInt(sg.Cells[i,j]);
sg.Canvas.Brush.Color:=clBlue;
end;
Canvas.FillRect(Rect);
Canvas.TextOut(Rect.Left+2, Rect.Top+2, sg.Cells[i, j]);
sg.Canvas.Brush.Color:=clBlack;
end;
end;

end.
мисс_граффити
вот здесь с границей массива ошибся:
Цитата
for i:=1 to ColCount do
cells[i,0]:=IntToStr(i);


но это к делу не относится...
а вот то, что заполняешь ты там только первую (нулевую) стороку, а потом (сразу же, до того, как пользователь получит возможность что-то ввести) пытаешься работать со всеми - очень даже.
вставляй в SGDrawCell обработку исключений или хотя бы проверку на заполненность.
volvo
Цитата
вот здесь с границей массива ошибся:
Где именно? Вообще-то границы грида определяются значениями (0 .. RowCount) по вертикали и (0 .. ColCount) по горизонтали, так что никакой ошибки не будет - все в пределах допустимого.
marwell
Цитата(мисс_граффити @ 7.04.2011 17:14) *

а вот то, что заполняешь ты там только первую (нулевую) стороку, а потом (сразу же, до того, как пользователь получит возможность что-то ввести) пытаешься работать со всеми - очень даже.
вставляй в SGDrawCell обработку исключений или хотя бы проверку на заполненность.

...
f:=False;
for t:=1 to 2 do begin
for i:=1 to sg.ColCount-1 do
if sg.Cells[i,t]='' then f:=true;
end;
if f=false then begin
for i:=1 to sg.ColCount-1 do begin
if StrToInt(sg.Cells[i,j+1])=StrToInt(sg.Cells[i,j+2]) then sg.Canvas.Brush.Color:=clRed;
...
если сделать так, будет неправильно? (извиняюсь, исправил, сначала ошибся)

Добавлено через 16 мин.
и еще, как я понял, вот эта строчка совсем не то что надо в данном случае
Canvas.TextOut(Rect.Left+2, Rect.Top+2, sg.Cells[i, j]);
volvo
Цитата
если сделать так, будет неправильно?
Нет...

Смотри. Событие OnDrawCell происходит при перерисовке каждой клетки. То есть, когда ты рисуешь одну клетку, тебе нужно всего навсего проверить ту, которая выше или ниже нее, одинаковый ли текст они содержат:
procedure TForm1.SGDrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect;
State: TGridDrawState);
begin
if (ACol > 0) and (ARow > 0) then // Если это условие не выполняется - то вообще ничего не делать
with Sender as TStringGrid do
begin

// Вот оно: проверяем, в текущей клетке не пусто? Нет? Тогда проверяем,
// текущая и та, что выше/ниже (если сейчас рисуется второй ряд, то 3 - 2 = 1,
// значит проверится то, что выше. Иначе: 3 - 1 = 2, проверится вышестоящая клетка)
// содержат одинаковый текст? Можно еще проверить, что там число, но я не стал этого делать
if (Cells[ACol, ARow] <> '') and (Cells[ACol, ARow] = Cells[ACol, 3 - ARow]) then
begin
Canvas.Brush.Color := clRed; // Да, содержимое одинаково, и непустое - красим
Canvas.Pen.Color := clWhite;
end
else
begin
Canvas.Brush.Color := clWhite; // Нет, ячейки либо разные либо пустые. Не красим...
Canvas.Pen.Color := clBlack;
end;

// Собственно, рисуем ячейки и текст в них.
Canvas.FillRect(Rect);
Canvas.TextOut(Rect.Left, Rect.Top, Cells[ACol, ARow]);
end;
end;

Вот. А теперь - еще кое что: как только ты вводишь информацию в ячейку, тебе надо перекрасить таблицу: во-первых, возможно, пары одинаковых значений больше нет, а во-вторых, максимум мог измениться. Что для этого надо? Я сделал обраотчик события OnSelEditText у грида:
procedure TForm1.SGSetEditText(Sender: TObject; ACol, ARow: Integer;
const Value: string);
begin
// Помечаем грид как невалидный, это приведет к его перерисовке
(Sender as TStringGrid).Invalidate;
end;


Вот в этом же самом событии, OnSelEditText, я бы и находил максимум, индекс макс. элемента, запоминал бы его в переменной, описанной в классе формы (не надо злоупотреблять глобальными переменными, лучше работать с членами класса), а в OnDrawCell добавил бы еще одну проверку: если сейчас рисуется ячейка, индекс ACol которой совпадает с индексом максимума - то рисовать ее не белым и не красным, а, скажем, зеленым... Попробуй это реализовать сам, если что не получится - я помогу.
marwell
кажется, я начал понимать
...
if f=false then begin
for i:=1 to sg.ColCount-1 do begin
if StrToInt(sg.Cells[i,j+1])=StrToInt(sg.Cells[i,j+2]) then begin sg.Canvas.Brush.Color:=clRed;
sg.Cells[i,j+1]:= sg.Cells[i,j+1];
sg.Cells[i,j+2]:=sg.Cells[i,j+2];
end;
...

содержимое ячеек постоянно перерисовывается, но тем же черным цветом

Добавлено через 2 мин.
volvo, спасибо большое, буду разбираться
marwell
добавил


unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Grids;

type
TForm1 = class(TForm)
SG: TStringGrid;
Edit1: TEdit;
procedure FormPaint(Sender: TObject);
procedure Edit1Change(Sender: TObject);
procedure SGDrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect;
State: TGridDrawState);
procedure SGGetEditText(Sender: TObject; ACol, ARow: Integer;
var Value: String);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;
im,max,i,j:integer;

implementation

{$R *.dfm}

procedure TForm1.FormPaint(Sender: TObject);
var i:integer;
begin
with SG do begin
cells[0,0]:='N';
cells[0,1]:='ai';
cells[0,2]:='bi';
for i:=1 to ColCount do
cells[i,0]:=IntToStr(i);
end;
end;

procedure TForm1.Edit1Change(Sender: TObject);
var i:integer;
begin
SG.ColCount := StrToInt((Sender as TEdit).Text);
for i:=1 to SG.ColCount do
SG.cells[i,0]:=IntToStr(i);
end;

procedure TForm1.SGDrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect;
State: TGridDrawState);
begin
if (ACol > 0) and (ARow > 0) then
with Sender as TStringGrid do
begin
if (Cells[ACol, ARow] <> '') and (Cells[ACol, ARow] = Cells[ACol, 3 - ARow]) then
begin
Canvas.Brush.Color := clRed;
Canvas.Pen.Color := clWhite;
end
else
begin
Canvas.Brush.Color := clWhite;
Canvas.Pen.Color := clBlack;
end;
if ACol=im then Begin
Canvas.Brush.Color := clGreen;
Canvas.Pen.Color := clWhite;
end;

Canvas.FillRect(Rect);
Canvas.TextOut(Rect.Left, Rect.Top, Cells[ACol, ARow]);
end;

end;

procedure TForm1.SGGetEditText(Sender: TObject; ACol, ARow: Integer;
var Value: String);
begin
(Sender as TStringGrid).Invalidate;
max:=0;
for j:=2 downto 1 do begin
for i:=1 to sg.ColCount-1 do begin
if (sg.Cells[ACol, ARow] <> '') and (StrToInt(sg.Cells[i,j])>max) and (sg.Cells[i,j]=sg.Cells[i,j+1]) then
begin max:=StrToInt(sg.Cells[i,j]);
im:=i;
end;
end;
end;

end;

end.


Цитата
запоминал бы его в переменной, описанной в классе формы (не надо злоупотреблять глобальными переменными, лучше работать с членами класса)
переменную im я описал так, как ты сказал?
volvo
Цитата
переменную im я описал так, как ты сказал?
Я имел в виду другое.

Смотри: (Показать/Скрыть)


Опять же, я не проверяю, введено ли число, если введешь строку - будет вылет программы, добавь проверку все-таки на численное значение, лучше использовать TryStrToInt вместо StrToInt...

В результате получается вот что:
marwell
Цитата(volvo @ 8.04.2011 15:38) *

Я имел в виду другое.

Смотри: (Показать/Скрыть)


Опять же, я не проверяю, введено ли число, если введешь строку - будет вылет программы, добавь проверку все-таки на численное значение, лучше использовать TryStrToInt вместо StrToInt...

В результате получается вот что:


А как работает TryStrToInt? (никогда прежде не сталкивался с ним)
volvo
var
value : Integer;

// ...
if TryStrToInt(Cell[i, 1], value) then
begin
// Все нормально, конвертация была успешной, в Value содержится число, можно его использовать
end
else // Упс... Строка не сконвертировалась в число, Value использовать нельзя... Можно сообщить об ошибке
marwell
Цитата(volvo @ 8.04.2011 19:35) *

var
value : Integer;

// ...
if TryStrToInt(Cell[i, 1], value) then
begin
// Все нормально, конвертация была успешной, в Value содержится число, можно его использовать
end
else // Упс... Строка не сконвертировалась в число, Value использовать нельзя... Можно сообщить об ошибке


теперь понятно. Спасибо огромное, завтра попробую дописать с учетом всех поправок
marwell
с TryStrToInt у меня проблема,никак не получается вставить его в нужное место, и так пробую, и эдак dry.gif А не проще ли использовать OnKeyPress и просто запретить ввод остальных символов, кроме чисел?
volvo
Цитата
А не проще ли использовать OnKeyPress и просто запретить ввод остальных символов, кроме чисел?
Это уж тебе решать, может оно и проще, а может и не совсем... Но в использовании TryStrToInt ничего сложного не вижу. Вот так, например:
procedure TForm1.SGSetEditText(Sender: TObject; ACol, ARow: Integer;
const Value: string);
var
i : integer;
IntValue : Integer; // <--- Раз ...
begin
max_index := -1;
with Sender as TStringGrid do
begin
// Проходим по всем ячейкам, и смотрим, равные ли значения в [, 1] и [, 2],
// и не пустые ли они. В общем, все, как и выше.
for i := 1 to ColCount do
if (Cells[i, 1] <> '') and (Cells[i, 1] = Cells[i, 2]) and TryStrToInt(Cells[i, 1], IntValue) then // <--- Два ...
begin
if max_index = -1 then // Если это первое непустое значение - то начинаем отсчет с него
max_index := i
else
// Если нет - то сравнваем с предыдущим максимумом...
if IntValue > StrToInt(Cells[max_index, 1]) then // <--- Три ...
max_index := i;
end; // if Cells ...
Invalidate; // <--- Ну, и перерисовываем...
end;
end;
, теперь даже если ты введешь не число (или число, но не целое, а вещественное), ничего страшного в этом обработчике не случится, все нецелые просто не будут обрабатываться (максимум среди них не будет искаться). Сделай что-то подобное в OnDrawCell - и можешь быть спокоен: вылетов не будет...
мисс_граффити
Цитата(volvo @ 7.04.2011 18:29) *

Где именно? Вообще-то границы грида определяются значениями (0 .. RowCount) по вертикали и (0 .. ColCount) по горизонтали, так что никакой ошибки не будет - все в пределах допустимого.

не поняла... кидаю на форму грид. свойства даже не трогаю.
colcount=5, rowcount=5 (по умолчанию).
считаю: 5 строк, 5 столбцов.
то есть номера 0,1,2,3,4. где пятый столбец и пятая строка? зачем в такие "невидимые" ячейки заголовок выводить?

делаю
StringGrid1.Cells[5,5]:='5';

ошибки не возникает, но и не появляется эта надпись нигде.
аналогичная реакция на обращение к [7, 7].
Более того: в результате выполнения:
StringGrid1.Cells[7,7]:='7';
ShowMessage(StringGrid1.Cells[7,7]);

показывается '7'...
никаких ошибок.
marwell
Цитата(volvo @ 11.04.2011 13:00) *

Это уж тебе решать, может оно и проще, а может и не совсем... Но в использовании TryStrToInt ничего сложного не вижу. Вот так, например:
procedure TForm1.SGSetEditText(Sender: TObject; ACol, ARow: Integer;
const Value: string);
var
i : integer;
IntValue : Integer; // <--- Раз ...
begin
max_index := -1;
with Sender as TStringGrid do
begin
// Проходим по всем ячейкам, и смотрим, равные ли значения в [, 1] и [, 2],
// и не пустые ли они. В общем, все, как и выше.
for i := 1 to ColCount do
if (Cells[i, 1] <> '') and (Cells[i, 1] = Cells[i, 2]) and TryStrToInt(Cells[i, 1], IntValue) then // <--- Два ...
begin
if max_index = -1 then // Если это первое непустое значение - то начинаем отсчет с него
max_index := i
else
// Если нет - то сравнваем с предыдущим максимумом...
if IntValue > StrToInt(Cells[max_index, 1]) then // <--- Три ...
max_index := i;
end; // if Cells ...
Invalidate; // <--- Ну, и перерисовываем...
end;
end;
, теперь даже если ты введешь не число (или число, но не целое, а вещественное), ничего страшного в этом обработчике не случится, все нецелые просто не будут обрабатываться (максимум среди них не будет искаться). Сделай что-то подобное в OnDrawCell - и можешь быть спокоен: вылетов не будет...

именно в OnDrawCell я и пытался сделать.
Я рассуждал так: сначала надо проверить, введено ли вообще значение
далее уже проверить, число ли это
потом уже остальные условия из OnDrawCell(проверка равны ли значения во 2ой и 3ей строке, проверка на максимальную пару)

procedure TForm1.SGDrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect;
State: TGridDrawState);
var IntValue : Integer;
begin
if (ACol > 0) and (ARow > 0) then
with Sender as TStringGrid do
begin
if (Cells[ACol, ARow] <> '') then
begin
if TryStrToInt(Cells[i, 1], IntValue) then
begin
if Cells[ACol, ARow] = Cells[ACol, 3 - ARow] then
begin
Canvas.Brush.Color := clRed;
Canvas.Font.Color := clWhite;
end
else
begin
Canvas.Brush.Color := clWhite;
Canvas.Font.Color := clBlack;
end;
if ACol = im then
begin
Canvas.Brush.Color := clGreen;
Canvas.Font.Color := clWhite;
end;

Canvas.fillRect(Rect);
Canvas.TextOut(Rect.Left, Rect.Top, Cells[ACol, ARow]);
end
else MessageDLG('Ошибка! Введено не число!',mtError,[mbOK],0);
end;
end;
end;

но, что-то я делаю не так
volvo
Я в твоем коде вижу несколько недочетов:
  1. Ветка else, где выводится MessageDlg, относится к самому первому If-у, так? То есть, она отрабатывает каждый раз, когда рисуются серые клетки (ARow = 0 или ACol = 0). Оно тебе надо, чтоб при каждой перерисовке грида выводилось несколько диалоговых сообщений. А вдруг какое-то из них перекроет собственно ячейку, и часть грида снова станет невалидной, опять начнет перерисовываться, и тогда что? Кандидатура на вечный цикл? Не надо этого счастья. Зачем тебе понадобилось сообщать при перерисовке, что "введено не число"? Просто не обрабатывай, если не число, и всего делов.
  2. Как реализована проверка. Я бы сделал ровно так же, как делал в OnSetEditText:

      if (ACol > 0) and (ARow > 0) then
    with Sender as TStringGrid do
    begin
    if (Cells[ACol, ARow] <> '') and (Cells[ACol, ARow] = Cells[ACol, 3 - ARow]) and
    TryStrToInt(Cells[ACol, ARow], IntValue) then
    begin
    Canvas.Brush.Color := clRed;
    Canvas.Font.Color := clWhite;
    end
    else
    begin
    Canvas.Brush.Color := clWhite;
    Canvas.Font.Color := clBlack;
    end;
    if (ACol = max_index) then // <--- Максимум?
    begin
    Canvas.Brush.Color := clGreen;
    Canvas.Font.Color := clWhite;
    end;
    Canvas.FillRect(Rect);
    Canvas.TextOut(Rect.Left, Rect.Top, Cells[ACol, ARow]);
    end;
    То есть, я даже не проверяю, чему равно содержимое клетки Cells[ARow, ACol]. Мне достаточно того, что содержимое верхней и нижней ячеек одинаковое, не пустое, и одно из них - число. Раз ячейки содержат одинаковые значения - значит, и второе число. Логично? Раз оба - одинаковых числа, то их надо что? Правильно, подсветить другим цветом... Вот и устанавливаем красную подсветку. А потом проверяем (это было и раньше в коде), "не максимальное ли значение"? Максимальное отмечаем зеленым цветом...
marwell
Цитата
Ветка else, где выводится MessageDlg, относится к самому первому If-у, так?

ээ, черт, что-то я совсем того ...
вот даже сейчас смотрю, и мне кажется что эта ветка относится сюда blink.gif
...
if TryStrToInt(Cells[i, 1], IntValue) then
...

видать надо выспаться
volvo
Цитата
видать надо выспаться
Надо просто лучше форматировать код. Считать end-ы не доставляет большого удовольствия...

В любом случае, лучше не выводить сообщения о нецелых, да еще и при отрисовке ячеек. Причину я озвучил выше.
marwell
volvo ,спасибо за помощь
Это текстовая версия — только основной контент. Для просмотра полной версии этой страницы, пожалуйста, нажмите сюда.