[ Миронченко ] Императивное и объектно-ориентированное програмирование на 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).
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.
