![](/user_photo/1319_hJRrZ.png)
[ Миронченко ] Императивное и объектно-ориентированное програмирование на Turbo Pascal и Delphi
.pdf331
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).
![](/html/1319/244/html_UCnnOaraXe.xg9k/htmlconvd-CzmPUH333x1.jpg)
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; |
Обработчик |
двойного |
щелчка левой |
|
кнопкой мыши |
|
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.