Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Ракитин Р.Ю. ООП в Turbo Delphi

.PDF
Скачиваний:
55
Добавлен:
18.03.2015
Размер:
3.59 Mб
Скачать

171

Назначение обработчиков событий

Вне зависимости от того, каким образом был создан компонент в визуальном построителе, или во время выполнения программы, он одинаковым образом интегрируется в общую схему работы, в том числе поддерживается и возможность использования событий. Ссылка на обработчик события компонента представляет собой свойство, которое имеет какой-либо процедурный тип данных.

Например, в нашем приложении можно организовать обработку события OnKeyPress компонента Edit1, создаваемого во время выполнения программы. Процедурный тип свойства OnKeyPress описан следующим образом:

туре

TKeyPressEvent = procedure (Sender: TObject; var Key: Char) of Object;

Следовательно, обработчик события должен являться методом какого-либо класса (так как использована конструкция of Object) и иметь два параметра: Sender типа TObject, и параметр-переменную Key типа Char.

Опишем такой метод в классе формы, поместив его заголовок в секцию private интерфейсной части, и реализовав его в описательной части модуля:

TForml

= class (TForm)

...

 

private

 

Edit1: TEdit;

Procedure EditEvent(Sender: TObject; var Key: Char);

...

Procedure TForml.EditEvent(Sender: TObject; var Key: Char);

begin

if Key = ‘х’ then Key := #0; end;

...

Описанный метод позволяет игнорировать нажатия на клавишу ‘х’. Для

назначения данного обработчика элементу управления следует занести ссылку на метод в свойство OnKeyPress, используя название метода. Сделаем это добавив в обработчик события OnClick кнопки Button1 строку:

Edit1.OnKeyPress:= EditEvent;

172

Глава 19. Определение собственных классов и работа с ними

Как отмечалось во второй главе, Delphi предлагает не только большое количество готовых классов, но и дает программисту обширный инструмент для разработки собственных объектов и классов. Рассмотрим пример создания структуры новых классов средствами языка Object Pascal.

Графический редактор «Окружности».

Пусть требуется разработать приложение, позволяющее рисовать на специальном поле окна окружности. Местоположение центра окружности должно определяться щелчком левой клавиши мыши. Радиус окружности и цвет контура должны настраиваться.

1. Разрабатываемое приложение легко декомпозируется на два объекта: окно приложения и изображаемая в нем окружность. Окно должно содержать интерфейсные элементы, позволяющие выполнять настройку параметров круга и саму процедуру рисования Edit, UpDown связанный с Edit, Button для выбора цвета и выхода, объект ColorDialog. Оно проектируется на базе класса TForm с использованием стандартных визуальных компонентов.

2. Создадим Класс TMyCircle в соответствии с правилами Delphi он наследуется от TObject. Этот класс добавляет поля для хранения координат центра окружности (х, у), ее радиуса r, цвета Color и имени компонента Image, на холсте которого нарисована окружность. Набор методов в основном определяется перечнем сообщений, на которые должен реагировать объект данного класса. Дополнительно переопределим метод Create, чтобы использовать его для инициализации полей объекта при создании, кроме этого

173

целесообразно определить метод Clear для стирания нарисованной окружности при изменении ее размера или цвета.

3. Определим тип доступа для всех компонентов класса: все поля и метод Clear, к которому не требуется доступ извне, объявим в секции private (внутренние), а все остальные компоненты в секции public.

4. Объявление класса TMyCircle выполним в отдельном модуле Circul:

unit Circul;

 

 

interface

 

 

Uses Extctrls, Graphics;

 

type

 

 

TMyCircle=class

 

 

private

 

 

x, y, r: Word;

//координаты центра и радиус окружности

Color: TColor;

//цвет

 

Image:TImage;

//поле для рисования

procedure Clear; //стирание окружности

public

 

 

constructor Create(almage: TImage; ax, ay, ar:Word;

aColor:TColor);

//конструктор

procedure Draw;

//рисование

procedure ReSize(ar: Word); //изменение размеров

procedure ReColor(aColor: TColor); //изменение цвета

end;

 

 

implementation

 

 

constructor TMyCircle.Create;

begin

 

 

inherited Create;

//вызвать наследуемый конструктор

Image:= aImage;

//инициализировать поле

x:=ax;

 

 

y:=ay;

 

 

r:=ar;

Color:= aColor; end;

procedure TMyCircle.Draw; begin

{задать цвет пера} Image.Canvas.Pen.Color:= Color; {нарисовать окружность} Image.Canvas.Ellipse(x-r, y-r, x+r, y+r);

end;

174

procedure TMyCircle.Clear; var

TempColor:TColor; begin

TempColor:= Color; //сохранить цвет пера

//фиксировать цвет фона

Color:= Image.Canvas.Brush.Color;

Draw; //нарисовать цветом фона – стереть

Color:= TempColor; //востановить цвет пера end;

procedure TMyCircle.ReSize; begin

Clear;

r:=ar;

Draw; end;

procedure TMyCircle.ReColor(aColor: TColor); begin

Clear;

Color:= aColor; Draw;

end; End.

5. Теперь переходим к программированию обработчиков событий, при наступлении которых должны выполняться основные операции приложения. Ниже приводится часть текста модуля Main, соответствующего форме

TMainForm. unit Unit1;

interface

...

implementation

{$R *.dfm} uses Circul; var

C:TMyCircle; //объявляем объект класса TMyCircle

{обработчик выбора цвета}

procedure TForm1.ButtonColorClick(Sender: TObject); begin

{если выполнен диалог выбора цвета, то} if ColorDialog1.Execute then

{установить цвет} Image1.Canvas.Pen.Color:=ColorDialog1.Color;

175

{если объект создан, то} if C<>nil then

{перерисовать другим цветом} C.ReColor(Image1.Canvas.Pen.Color);

end;

{обработчик кнопки Выход}

procedure TForm1.ButtonExitClick(Sender: TObject); begin

Application.Terminate; end;

{выполняется при активации формы}

procedure TForm1.FormActivate(Sender: TObject); begin

{установить белый фон} Image1.Canvas.Brush.Color:= clWhite; {установить цвет рисования} Image1.Canvas.Pen.Color:= clBlack;

end;

{обработчик нажатия мышью на объекте Image} procedure TForm1.Image1MouseDown(Sender: TObject;

Button: TMouseButton; Shift: TShiftState; X, Y: Integer);

begin

{если нажата левая клавиша мыши} if Button=mbLeft then

begin

{если объект создан, то уничтожить его} C.Free;

{конструировать}

C:= TMyCircle.Create(Image1, X, Y, StrToInt(EditR.Text), Image1.Canvas.Pen.Color);

{изобразить объект с заданными параметрами} C.Draw;

end; end;

{изменение радиуса круга}

procedure TForm1.UpDown1Click(Sender: TObject; Button: TUDBtnType);

begin

{если объект создан, то перерисовать с другим радиусом } if C<>nil then C.ReSize(StrToInt(EditR.Text));

end;

176

initialization

{Последний используемый объект уничтожается при закрытии формы}

finalization

C.Free; end.

Из приведенного текста видно, что конкретный объект класса TMyCircle создается при нажатии левой клавиши мыши. Если объект уже существовал, то он уничтожается. Соответствующая уничтоженному объекту окружность на холсте сохраняется, но теряется связь изображения и объекта. После этого мы больше не можем менять цвет и размер этой окружности. Таким образом, одна и та же переменная

используется многократно для хранения адресов различных объектов при рисовании

6.Добавьте возможность изменять толщину и тип линии окружности.

Использование абстрактных методов (графический редактор «Окружности и квадраты»).

Изменим пример предыдущего раздела так, чтобы можно было рисовать не только окружности, но и, например, квадраты.

Естественно, теперь интерфейс должен обеспечивать возможность выбора фигуры. С этой целью добавим в форму компонент TRadioGroup. При обработке события нажатия мышью на объекте Image необходимо учитывать тип выбранной фигуры.

Класс TMySquare можно построить несколькими способами:

1.Класс ТМуSquare можно наследовать от TObject так же, как был получен класс ТМуСirсlе. В этом случае нам придется повторить программирование всех методов данного класса, что явно нецелесообразно.

2.Класс TMySquare можно наследовать от TMyCircle. Тогда в дочернем классе будут доступны методы TMyCircle Create, Clear, ReColor, ReSize,

определяющие общие элементы поведения. Метод Draw необходимо объявить виртуальным, так как он вызывается из наследуемых методов и переопределен в классе-потомке. Метод Draw класса TMySquare должен быть объявлен переопределенным override. Данный вариант наследования, принципиально применимый в конкретном случае, является неуниверсальным (и, в общем, нелогичным: как можно «наследовать» от круга квадрат?).

3.С учетом существования двух типов объектов со сходным поведением можно создать абстрактный класс TFigure, инкапсулирующий требуемый тип

поведения фигур. В этом классе нужно объявить метод Draw виртуальным (virtual) и абстрактным (abstract) и определить методы Create, Clear, ReColor, ReSize через метод Draw. Теперь мы можем наследовать от этого

177

абстрактного класса классы, рисующие любые фигуры. Эти классы должны будут переопределять абстрактный метод Draw класса TMyFigure.

Ниже представлен текст модуля Figure. unit Figure;

interface

uses Extctrls, Graphics;

type

TMyFigure=class private

x, y, r:Word; Color: TColor; Image :TImage; procedure Clear;

public

constructor Create(almage: TImage; ax, ay, ar: Word; aColor:TColor);

{абстрактная процедура}

procedure Draw; virtual; abstract; procedure ReSize(ar: Word); procedure ReColor(aColor:TColor);

end;

{класс Окружность} TMyCircle=class(TMyFigure) public

procedure Draw; override; //рисование окружности end;

{класс Квадрат} TMySquare=class (TMyFigure) public

procedure Draw; override; //рисование квадрата end;

implementation

constructor TMyFigure.Create; begin

inherited Create; Image:=aImage; x:=ax;

y:=ay;

r:=ar;

Color:=aColor; end;

178

procedure TMyFigure.Clear; var

TempColor:TColor; begin

TempColor:= Color;

Color:= Image.Canvas.Brush.Color;

Draw; {нарисовать фигуру цветом фона - стереть} Color:=TempColor;

end;

procedure TMyFigure.Resize; begin

Clear;

r:=ar;

Draw; end;

procedure TMyFigure.Recolor; begin

Clear;

Color:= aColor; Draw;

end;

procedure TMyCircle.Draw; begin

Image.Canvas.Pen. Color:=Color; Image.Canvas.Ellipse(x-r, y-r, x+r, y+r);

end;

procedure TMySquare.Draw; begin

Image.Canvas.Pen.Color:=Color; Image.Canvas.Rectangle(x-r, y-r, x+r, y+r);

end; End.

Задания для самостоятельного выполнения:

1.Создайте приложение с использованием модуля Figure.

2.Добавьте возможность изменять толщину и тип рисуемой линии фигуры.

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

179

Глава 20. Многопоточность

Понятие и назначение потоков

Поток это последовательность каких-либо команд и вызовов подпрограмм. В каждой программе, таким образом, имеется как минимум один поток, называемый основным, однако могут быть созданы и дополнительные потоки, выполняемые параллельно основному.

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

Даже если поток выполняет бесконечный цикл, он не может «подвесить» систему или снизить скорость ее работы. Еще один интересный нюанс многопоточных приложений состоит в том, что при некоторых долгосрочных операциях не используются ресурсы процессора. Такой операцией, например, является запись информации на диск.

Поток выполнения является системным понятием и поддерживается непосредственно средствами Windows. В Delphi свойства и методы потоков инкапсулированы в класс TThread (англ. Thread нить, поток), в котором заявлен абстрактней метод Execute без параметров, как раз и представляющий собой последовательность команд, выполняемых в потоке:

procedure Execute; virtual; abstract;

Для создания дополнительного потока в программе следует реализовать класс-наследник TThread, переопределив метод Execute.

В среде Delphi поток можно реализовать в виде отдельного модуля. Для этого необходимо выбрать в меню File New Other Thread Object. После

нажатия на ярлыке появиться окно, в котором указывается имя нового класса:

180

После этого будет создан новый модуль, содержащий следующий код: unit Unit1;

interface

uses

Classes;

type

TCalcThread = class(TThread) private

{ Private declarations } protected

procedure Execute; override; end;

implementation

{ Важно: Методы и свойства визуальных объектов могут быть вызваны только в методе Synchronize, например,

Synchronize(UpdateCaption);

иUpdateCaption должен быть реализован по примеру,

procedure TCalc.UpdateCaption; begin

Form1.Caption := 'Обновление в потоке'; end; }

{TCalcThread }

procedure TCalcThread.Execute; begin

{ Поместите сюда код потока } end;

end.

По ходу модуля Delphi вставляет необходимые комментарии.

Для использования созданного модуля необходимо подключить его в основном модуле.

Описание потока не приводит к выполнению каких-либо действий. Для того чтобы потоку было передано управление, должен быть создай экземпляр потокового класса, заполнены внутренние свойства, необходимые для выполнения вычислений, после чего поток запускается. Например: