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

[ Миронченко ] Императивное и объектно-ориентированное програмирование на Turbo Pascal и Delphi

.pdf
Скачиваний:
68
Добавлен:
25.04.2014
Размер:
3.16 Mб
Скачать

331

20:{ Private declarations }

21:public

22:{ Public declarations }

23:end;

24:

25:var

26:Form1: TForm1;

28:implementation

30:{$R *.dfm}

32:procedure TForm1.JaClick(Sender: TObject);

33:begin

34:Label2.Visible:=true;

35:end;

36:

37:procedure TForm1.NeinClick(Sender: TObject);

38:begin

39:Label3.Visible:=true;

40:end;

41:

42:procedure TForm1.NeinMove(Sender: TObject; Shift: TShiftState; X,Y: Integer);

43:var

44:i,n:integer;

45:begin

46:n:=15;

47:with Nein do {with работает и для объектов}

48:begin

49:if (y<=Height div 2) then {сдвигаемся по вертикали}

50:begin

51:for i:=1 to n do

52:Top:=Top+1;

53:end

54:else

55:begin

56:for i:=1 to n do

57:Top:=Top-1;

58:end;

59:if (x<=Width div 2) then {сдвигаемся по горизонтали}

60:begin

61:for i:=1 to n do

62:Left:=Left+1;

63:end

64:else

65:begin

66:for i:=1 to n do

67:Left:=Left-1;

68:end;

69:end;

70:end;

332

71:

72: end.

В процедуре NeinMove есть параметр

Shift: TShiftState

Он показывает, какие из управляющих клавиш (Alt, Shift, Ctrl) были нажаты. Но в данном обработчике нам эти параметры не нужны.

17.6. Установка пароля на программу

Во многих примерах, которые мы будем писать в дальнейшем, мы не будем изменять файл проекта. Поэтому у вас может появиться ощущение, что этот файл для оконных приложений священен, и прикасаться к нему нельзя. Чтобы у вас такого чувства не возникало, мы напишем программу, при входе в которую проверяется пароль (он вводится с помощью диалогового окна).

Пример 4: Защита программы паролем (приводится лишь dpr-файл, а файл с формой я оставил тот, который был по умолчанию).

1 : program Kennwort;

2 :

3 : uses

4 : Forms,

5 : Dialogs,

6 : haupt in 'haupt.pas' {Form1};

7 :

8 : {$R *.res}

9 : var

10:Mel:string; {Meldung - сообщение}

11:begin

12:Mel:=InputBox('','Введите пароль','');

13:if Mel<>'Anders' then

14:begin

15:ShowMessage('Неверный пароль');

16:exit;

17:end;

18:Application.Initialize;

19:Application.CreateForm(TForm1, Form1);

20:Application.Run;

21:end.

17.7. Динамическое создание компонентов

Сейчас мы запрограммируем калькулятор, который сможет работать с числами в разных системах счисления. Для того чтобы обозначать цифры в системах, где основание больше десяти, мы будем использовать заглавные латинские буквы. Т.к. их только 26, то возможные основания ССч могут колебаться от 2 до 36. Для красоты мы сделаем так, чтобы на форме никогда не находились лишние клавиши, причем при уменьшении основания ССч будет увеличиваться размер клавиш (см. рис. 17.4).

333

Рис 17.4 Форма для калькулятора

Из операций мы рассмотрим лишь сложение и вычитание. Другие операции вы легко сможете добавить сами. Для того чтобы написать эту программу, надо больше узнать о свойствах компонентов. Единственной алгоритмической трудностью является перевод чисел из одной ССч в другую – но эта задача для нас элементарна. Трудности иного плана возникнут при динамическом создании/удалении компонентов, - раньше мы весь интерфейс формы делали в Object Inspector, а теперь нам придется поработать вручную.

17.8.Общие свойства компонентов

Компонентом называется любой класс-наследник класса TComponent.

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

У каждого компонента есть следующие свойства:

property Name: TComponentName;

Имя компонента

property Owner: TComponent;

Владелец компонента

property Components[Index:

Список компонентов, владельцем которых

Integer]: TComponent;

является компонент

property ComponentIndex:

Индекс компонента в списке Components

Integer;

своего владельца

property ComponentCount:

Количество компонентов, которыми владеет

Integer;

данный компонент

Конструктор у класса TComponent имеет следующий вид: constructor Create(AOwner: TComponent); virtual;

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

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

334

Вообще говоря, компоненты не обязательно могут быть видимыми – например, диалоговые компоненты. Все компоненты, которые могут быть нарисованы, являются наследниками класса TControl, который, в свою очередь, является прямым наследником класса TComponent.

17.9. Свойства элементов управления (TControl)

Некоторые вы уже знаете – Left, Top, Height, Width.

Мы рассмотрим еще некоторые другие:

property Visible: Boolean;

True – если компонент видимый

property Text: TCaption;

Текстовое поле, связанное с компонентом

property Caption: TCaption;

Заголовок

property Font: TFont;

Шрифт, которым будет прорисовываться

 

Caption

property Parent: TWidgetControl;

Родитель компонента

property Color: TColor;

Цвет заголовка

Родителями могут быть только объекты класса TWingetControl (это – наследник TControl). 2 важных дополнительных свойства этого класса приведены ниже:

property

ControlCount: Integer;

Количество элементов массива Controls

property

Controls[Index:

Массив элементов, которыми управляет

Integer]: TControl;

компонент

В конструкторе компонентов свойство Parent не устанавливается автоматически, поэтому если вы хотите назначить родителя компонента, то надо записать в свойство Parent ссылку на родителя.

17.10. События мыши и клавиатуры

Обработчик события – это свойство процедурного типа. Когда мы создавали обработчики события в Object-Inspector, автоматически создавалась процедураобработчик и указатель на нее записывался в соответствующее свойство компонента.

Если мы хотим динамически создавать компоненты, то надо создавать обработчики событий вручную.

Давайте рассмотрим 5 основных обработчиков событий:

property OnMouseDown: TMouseEvent;

Обработчик нажатия кнопки мыши

property OnClick: TNotifyEvent;

Обработчик

щелчка

левой кнопкой

 

мыши

 

 

property OnMouseMove:

Перемещение

указателя мыши по

TMouseMoveEvent;

компоненту

 

 

property OnMouseUp: TMouseEvent;

Обработчик отпускания кнопки мыши

property OnDblClick: TNotifyEvent;

Обработчик

двойного

щелчка левой

 

кнопкой мыши

 

335

type

TMouseEvent = procedure (Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer) of object;

TMouseMoveEvent = procedure(Sender: TObject; Shift: TShiftState; X, Y: Integer) of object;

TNotifyEvent = procedure (Sender: TObject) of object;

Sender содержит ссылку на компонент, над которым находился указатель мыши, X,Y – координаты указателя мыши в момент возникновения события.

Для определения, какая кнопка была нажата, есть спец. перечислимый тип

TMouseButton.

type TMouseButton = (mbLeft, mbRight, mbMiddle);

TShiftState используется для получения специальной информации:

type TShiftState = set of (ssShift, ssAlt, ssCtrl, ssLeft, ssRight, ssMiddle, ssDouble);

ssShift была нажата клавиша Shift

ssAlt была нажата клавиша Alt

ssCtrl была нажата клавиша Ctrl

ssLeft была нажата левая кнопка мыши

ssRight была нажата правая кнопка мыши

ssMiddle была нажата средняя кнопка мыши

ssDouble были нажаты одновременно левая и правая кнопки мыши

Чтобы написать обработчик события, надо написать процедуру соответствующего типа, и просто присвоить в соответствующее свойство адрес этой процедуры.

17.11. TComboBox

Для выбора основания ССч используется компонент TComboBox (находится во вкладке Standard).

У TComboBox есть свойство Items класса TStringList, в котором хранятся элементы, которые можно выбирать в комбосписке.

17.12. Описание работы калькулятора (Пример № 5)

Весь код я приводить здесь не буду. Мы рассмотрим лишь основные части проекта.

Подпрограммы, которые необходимы для написания калькулятора, можно разбить на 3 группы:

1.Подпрограммы, которые преобразуют числа из одной ССч в другую.

2.Создание кнопок и управление их размерами.

3.Обработчики события OnClick для всех кнопок.

4.Вспомогательные обработчики (FormCreate, FormGrundSelect).

Первую группу функций мы рассматривать не будем, - вы и сами разберетесь, что к чему.

336

Поля калькулятора

private

All:integer; //максимальное количество цифр KnNum:integer; //индекс первой кнопки

OperQuant:integer;//количество кнопок со знаками и операциями OperInd:integer; //индекс, с которого начинаются операции Zahl1:integer; //хранятся промежуточные числа WelcheZahl:integer; //какое слагаемое сейчас вводится R:TRect; //прямоугольник, в котором будут расположены цифры R2:TRect; //прямоугольник, в котором будут расположены знаки

операций

VorherOper:integer; //индекс предыдущей операции, которая была выполнена

//0 - неарифметич. операция (нажатие цифры), 1 - сложение,

//2 - вычитание,3 - после выдачи равенства LaufGrund:integer; //текущее основание системы счисления

Теперь будут приведены основные процедуры.

2-я группа функций

// Создает кнопки и заполняет их начальными значениями (кроме координат)

procedure SchaffeKnopfe; //Прячет все кнопки с цифрами procedure VerbergenKnopfe;

//Расставляет внутри прямоугольника R N кнопок, которые

//находятся в массиве Controls, начиная с номера ContrInd. procedure Stellknopfe(R:TRect;N:integer;ContrInd:integer);

procedure TForm1.SchaffeKnopfe; var

k:integer;

bt:TButton; begin

All:=36; //сколько кнопок с цифрами KnNum:=self.ControlCount;//индекс первой кнопки совпадает с текущим

//количеством дочерних компонентов формы

OperQuant:=3;// '+' '-' '='

OperInd:=KnNum+All; // индекс первой Оперкнопки

//создаем кнопки и добавляем их в список Controls формы for k:=1 to All+OperQuant do

begin bt:=TButton.Create(self); bt.Parent:=self;

bt.Visible:=false; //изначально делаем все кнопки невидимыми end;

for k:=KnNum to KnNum+All-1 do //заполняем значениями кнопки

337

begin

if k<10+KnNum then begin

Controls[k].Name:='kn'+IntToStr(k-KnNum); TButton(Controls[k]).Caption:=IntToStr(k-KnNum); end

else begin

TButton(Controls[k]).Caption:=Chr(65+k-KnNum-10); Controls[k].Name:='kn'+Chr(65+k-KnNum-10);

end;

//устанавливаем обработчик нажатия на Цифрокнопку

TButton(self.Controls[k]).OnClick:=ZifferKlick; end;

//Изменяем параметры Оперкнопок

Controls[operInd].Name:='knPl'; TButton(Controls[operInd]).Caption:='+'; TButton(Controls[operInd]).OnClick:=PlusKlick;

Controls[operInd+1].Name:='knMin';

TButton(Controls[operInd+1]).Caption:='-';

TButton(Controls[operInd+1]).OnClick:=MinusKlick;

Controls[operInd+2].Name:='knGl'; TButton(Controls[operInd+2]).Caption:='='; TButton(Controls[operInd+2]).OnClick:=GleichKlick;

end;

procedure TForm1.Stellknopfe(R:TRect;N:integer;ContrInd:integer); var

i,j,k,a,b,aGut,bGut,diff:integer;

Lan,Hohe:integer;

ZwRaum:integer; begin

ZwRaum:=3; //расстояние между кнопками

//Ищем aGut, bGut - количество кнопок по вертикали и горизонтали соответсвенно //Эти числа выбираются так, чтобы разница между ними была наименьшей.

diff:=All;

for b:=1 to All do

for a:=b downto 1 do begin

if ((b-a)>diff) or (b*a<n) then break;

if b*(a-1)<n then begin

if b-a<diff then begin

aGut:=a;

bGut:=b; diff:=b-a;

338

end;

break;

end;

end;

Lan:=(R.Right-R.Left) div bGut;//длина кнопки (с межкнопочным интерв.)

Hohe:=(R.Bottom-R.Top) div aGut;//высота кнопки (с межкнопочным интерв.)

k:=ContrInd; //индекс первой кнопки for i:=1 to aGut do

for j:=1 to bGut do begin

Controls[k].Width:=Lan-ZwRaum; // устанавливаем размеры Controls[k].Height:=Hohe-ZwRaum; Controls[k].Left:=R.Left+ZwRaum+(j-1)*Lan; Controls[k].Top:=R.Bottom-ZwRaum-i*Hohe;

TButton(Controls[k]).Font.Name:='times New Roman';//назв.

шрифта

TButton(Controls[k]).Font.Height:=Hohe div 2; //высота буквы self.Controls[k].Visible:=true; //делаем кнопку видимой k:=k+1;

if k=Contrind+n then //если уже установили все нужные кнопки exit;

end;

end;

3-я группа обработчики нажатий на кнопки

Рассмотрим обработчики лишь для Цифрокнопки и Плюсокнопки.

Имя кнопки начинается с 2 букв – kn (der Knopf - кнопка), после которых следует цифра, которая стоит в заголовке кнопки.

Алгоритм обработчика ясен: если до этого было нажато равенство, то надо вводить новое число, поэтому старое число стирается из поля ввода и заменяется нажатой цифрой. Если предыдущая операция – не равенство, значит надо добавить новую цифру к числу, которое стоит в поле ввода. Кроме того, если в поле ввода стоит ноль, то нажатие новых нулей не изменяет поля ввода.

procedure TForm1.ZifferKlick(Sender:TObject); begin

if VorherOper=3 then //Если предыдущая операция - равенство begin

EingFeld.Text:=TButton(Sender).Name[3]; //добавляем цифру vorheroper:=0; //фиксируем, что была нажата клавиша

end else

if EingFeld.Text='0' then EingFeld.Text:=TButton(Sender).Name[3]

else

339

EingFeld.Text:=EingFeld.Text+TButton(Sender).Name[3]; //добавляем цифру

end;

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

procedure TForm1.PlusKlick(Sender:TObject); begin

case welcheZahl of 1:

begin Zahl1:=JedeStrZuInt(EingFeld.Text,LaufGrund); EingFeld.Text:='0';

WelcheZahl:=2;

end;

2:

begin

if EingFeld.Text<>'0' then begin

Zahl1:=Zahl1+JedeStrZuInt(EingFeld.Text,LaufGrund);

EingFeld.Text:='0';

end;

end;

end;

VorherOper:=1;

end;

Вспомогательные функции

Так выглядит обработчик выбора нового основания ССч в Grund. procedure TForm1.FormGrundSelect(Sender: TObject);

var etw:integer;

begin VerbergenKnopfe;

etw:=JedeStrZuInt(EingFeld.Text,LaufGrund);

LaufGrund:=StrToInt(Grund.Items[Grund.ItemIndex]);

EingFeld.Text:=AusDezimZuJede(etw,LaufGrund);

self.Stellknopfe(R,LaufGrund,KnNum);

end;

Начальные установки формы:

procedure TForm1.FormCreate(Sender: TObject); var

i:integer; begin

WelcheZahl:=1; //вводим сначала первое число

340

VorherOper:=3; //считаем, что изначально было равенство

Zahl1:=0;

//Устанавливаем начальные координаты кнопок с цифрами

R.Bottom:=height-30; R.Right:=width-160; R.Left:=5; R.Top:=55;

//Устанавливаем начальные координаты кнопок со знаками операций

R2.Left:=R.Right+30; R2.Right:=width-20; R2.Top:=R.Top;

R2.Bottom:=R.Bottom; //создаем кнопки

self.SchaffeKnopfe;

self.Stellknopfe(R,{Grund.ItemIndex}8+2,KnNum);//расставляем цифры self.Stellknopfe(R2,OperQuant,OperInd); //расставляем

операции

EingFeld.Text:='0'; //заполняем текстовое поле

//заполняем ComboBox for i:=2 to All do

Grund.Items.Add(IntToStr(i));

Grund.ItemIndex:=8;

LaufGrund:=Grund.ItemIndex+2;//Текущее основание ССч end;

И, наконец, хотелось бы, чтобы нельзя было изменять размеры формы.

Для того чтобы нельзя было растягивать форму, надо изменить в Object Inspector свойство формы BorderStyle – установить значение bsSingle.

Чтобы нельзя было нажать клавишу «развернуть», можно зайти в разделе

Properties во вкладку BorderIcons, и установить значение biMaximize равным False.

Вот мы и написали калькулятор!

17.13. Исключения

Исключительные ситуации (исключения) возникают, когда программа пытается выполнить некорректную операцию, например: деление на ноль, вычисление корня из отрицательных чисел (имеются в виду действительные и целые числа), попытка открыть несуществующий файл и т.п. В TP исключения нельзя было обрабатывать, что порождало ряд трудностей. Например, если программа не могла открыть несуществующий файл, то она прекращала свою работу и возвращала код ошибки. В Delphi обрабатывать исключительные ситуации можно, что мы и должны научиться делать.

Базовый класс для обработки исключений – класс Exception, являющийся прямым потомком класса TObject. Все классы-исключения являются наследниками класса Exception.