Управление событиями, или Калькулятор — это маленький компьютер

Задачи урока

  • Работа с компонентом SpeedButton.
  • Назначение одного события нескольким компонентам.
  • Приведение типов.

Выполнение упражнения

В этом уроке мы напишем облегчённый вариант калькулятора, аналогичный тому, что есть в Windows (рисунок 1). Он будет проводить четыре арифметических действия с целыми числами. Обозначим функциональность нашего калькулятора:

  • отображает в однострочном редакторе число, которое вводится при помощи цифровых кнопок;
  • при нажатии на кнопку типа операции она запоминается;
  • при нажатии на кнопку «=» отображается результат операции над числами.

image001
Рисунок 1. Окно приложения

1. Дизайн приложения

  1. Расположите компоненты согласно рисунку 2а и задайте значение их свойства согласно рисунку 2б. В сносках обозначены порядковые номера компонентов SpeedButton.

а)image002 б) image004
Рисунок 2

Здесь мы столкнёмся с новым компонентом  — кнопкой SpeedButtonimage005 (вкладка Additional). Эта кнопка аналогична компоненту Button, но имеет некоторые особенности.

  1. Измените имена компонентов следующим образом:
Имя, которое

было

Имя, на которое

необходимо изменить

Что это означает?
Edit1 EditOut Однострочный редактор для вывода результата
SpeedButton1  —

SpeedButton10

SBtn1 — SBtn10 Цифровые кнопки
SpeedButton11 SBtnRavno Кнопка  «равно»
SpeedButton12 SBtnС Кнопка сброса
SpeedButton13 SBtnPlus Кнопка  «плюс» Определяют тип

операции над числами

SpeedButton14 SBtnMinus Кнопка  «минус»
SpeedButton15 SBtnMult Кнопка  «умножить»
SpeedButton16 SBtnDiv Кнопка  «разделить»

Отметим, что имя компонента SpeedButton изменено на его аббревиатуру  — SBtn, т. к. оно слишком длинное.

 

2. Цифровые кнопки калькулятора

  1. Для создания калькулятора прежде всего объявите глобальные переменные (обозначены жёлтым цветом):
var
  Form1: TForm1;
  A,B:Real;      // Числа, с которыми будут выполняться арифметические действия
  Result:Real;   // Результат математической операции: Result = A <мат.операция, определяемая переменной typ> B
  typ:Integer;   // Тип операции над числами: * / - +

  implementation
  {$R *.dfm}

 

Листинг 1

Для чего нужна каждая переменная, понятно из комментариев.

  1. Напишите обработку события кнопки с заголовком «1» таким образом, чтобы при её нажатии в однострочном редакторе отображалась соответствующая цифра. Для этого обработайте событие OnClick от кнопки SBtn1 следующим образом:
procedure TForm1.SBtn1Click(Sender: TObject);
begin

  EditOut.Text:=EditOut.Text + SBtn1.Caption;

end;

 

Листинг 2

Эта запись означает, что в компонент EditOut к его существующему содержимому будет добавлен заголовок первой кнопки (т. е. «1»).

  1. Запустите программу и нажмите несколько раз на кнопку «1». В компоненте EditOut происходит отображение заголовка кнопки (рисунок 3).

image006
Рисунок 3

  1. Чтобы при нажатии на другие цифровые кнопки происходило отображение их заголовка, необходимо обработать событие OnClick для каждой кнопки. Но… это практически один и тот же код (отличие только в названии кнопки)!!! Поэтому такие действия непрактичны. Намного проще запрограммировать один обработчик события для всех кнопок, и в нём определять, на кнопку с каким заголовком нажал пользователь. Но как это сделать?

Прежде всего, всем цифровым кнопкам (кроме кнопки «1») назначьте обработчик события OnClick кнопки SBtn1. Для этого выделите все цифровые кнопки (через клавишу Shift), кроме первой (рисунок 4а), перейдите в окно Инспектор объектов на вкладку Events и раскройте выпадающий список напротив события OnClick (рисунок 4б). Появится список всех созданных событий для компонентов формы, которые имеют такой же формат процедуры, как и у выбранного компонента (либо компонентов). В нашем случае здесь находится всего одна процедура обработки события OnClick первой кнопки. Выберите её.

Перейдите к окну конструктора формы и выполните двойной щелчок на каждой цифровой кнопке. При этом убедитесь, что всё время происходит обращение к одной и той же процедуре SBtn1Click.

а)image007 б) image009
Рисунок 4

  1. Запустите программу и пощёлкайте на цифровых кнопках. Они работают все, но в компоненте EditOut выводится «1» в любом случае.

 

3. Но цифры-то разные?

  1. Итак, первый шаг мы сделали. Теперь нажатие на любую цифровую клавишу обрабатывается одной и той же процедурой. Осталось выяснить, от какой именно кнопки пришло событие.

Ранее мы говорили, что в любой обработчик события передаётся обязательный параметр Sender, типа TObject. Этот параметр как раз и содержит информацию о компоненте, от которого пришло событие. Для выполнения каких-либо операций с этим параметром необходимо привести его к типу того компонента, для которого выполняются операции. Выглядит это следующим образом:

<Тип> (Sender)

Например, приведенные ниже две записи события OnClick кнопки SBtn1 типа TSpeedButton будут функционально аналогичны (приведение типов выделено жёлтым цветом):

editOut.Text:=editOut.Text + SBtn1.Caption;
editOut.Text:=editOut.Text + TSpeedButton(Sender).Caption;

 

Листинг 2

Воспользуемся этим чудесным параметром и перепишем обработчик события SBtn1Click следующим образом:

procedure TForm1.SBtn1Click(Sender: TObject);
begin

  editOut.Text := editOut.Text + TSpeedButton(Sender).Caption;

end;

Листинг 3

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

  1. Запустите программу и пощёлкайте по цифровым кнопкам. Теперь они работают правильно!

 

4. Функциональные клавиши калькулятора

  1. Следующая задача — это выполнение операций над введенными числами. Порядок работы с калькулятором должен быть следующий:
  • ввод первого числа «A»;
  • нажатие на кнопку типа операции;
  • ввод второго числа «B»;
  • нажатие на кнопку «=» для получения результата.

Ввод чисел мы уже реализовали. Теперь необходимо запомнить тип операции после нажатия пользователем на соответствующую кнопку (+ – * /). Но как мы определим, на какой из типов операций мы нажали?

Здесь просмотр заголовков функциональных кнопок, как было в случае с цифровыми кнопками калькулятора, нецелесообразен. Причины рассматриваются ниже. Лучше использовать какой-то другой параметр компонента. Идеальным вариантом в этом случае будет свойство, которое есть абсолютно у всех компонентов  — Tag, типа Integer. Оно изначально никаких действий над компонентом не производит, и было введено разработчиками среды Delphi для нужд программистов. Присвойте значения для свойства Tag кнопкам операций, как показано на рисунке 5.

image010
Рисунок 5

Таким образом, каждой операции будет присвоен личный номер.

  1. Как и в предыдущем случае, реализуем одну обработку события OnClick для всех арифметических операций. Для этого создайте обработчик OnClick для кнопки SBtnPlus и напишите в нём следующий код:
procedure TForm1.SBtnPlusClick(Sender: TObject);
begin

  A := strtofloat( EditOut.text );
  Typ := TSpeedButton( Sender ).Tag;
  EditOut.Clear;

end;

Листинг 4

Поясним код:

  • запоминаем число, введенное в однострочный редактор в переменной A;
  • запоминаем операцию в переменной Typ;
  • очищаем однострочный редактор для ввода второго числа.
  1. Выделите кнопки – * / и через Инспектор объектов присвойте их события OnClick событию SBtnPlusClick.
  2. Теперь обработайте событие кнопки «=» следующим образом:
procedure TForm1.SBtnRavnoClick(Sender: TObject);
begin

  B := strtofloat( EditOut.text );
  Case typ of
    0: Result := A + B;
    1: Result := A - B;
    2: Result := A * B;
    3: Result := A / B;
  end;
  A := Result;
  EditOut.Text := floattostr( Result );

end;

Листинг 5

Поясним код:

  • запоминаем второе число в переменную B;
  • вычисляем результат, исходя из типа операции с помощью оператора выбора Case of (выделено жёлтым). Обратите внимание на то, что если бы мы считывали тип операции с названий кнопок, то данная процедура была бы куда более громоздкой, т. к. мы не смогли бы воспользоваться оператором выбора (он не работает с типом String). Для каждого типа операций пришлось бы записывать оператор If. Результат операции заносится в переменную Result;
  • переменной A присваиваем значение результата для дальнейших арифметических действий;
  • отображаем результат в однострочном редакторе EditOut.
  1. Обработайте событие кнопки «C» следующим образом:
procedure TForm1.SBtnCClick(Sender: TObject);
begin

  EditOut.Clear;

end;

Листинг 6

Эта кнопка выполняет очистку компонента EditOut.

  1. Запустите приложение. Введите 10 и нажмите на кнопку «+», введите 5 и нажмите на кнопку «=». Получите на экране 15. Проверьте остальные арифметические операции. Как видите, калькулятор — это просто!

 

5. Коротко о главном

Если имя компонента слишком длинное, то лучше его сократить.

Глобальные переменные объявляются перед разделом Implementation. Они видны во всём модуле.

Любое событие компонента можно привязать к аналогичному событию другого компонента.

Параметр Sender содержит информацию о типе компонента, от которого пришло событие.

Для выполнения с этим параметром Sender каких-либо операций, необходимо привести его к типу того компонента, для которого выполняются эти операции.

Свойство Tag типа Integer есть у всех компонентов. Оно было введено разработчиками среды Delphi для нужд программистов.

6. Выполнение заданий

Задания I уровня сложности

1. Модифицируйте созданный калькулятор так, чтобы он вычислял обратное значение числа (кнопка «1/x») и корень (кнопка «sqrt»). Если вычисление невозможно, то вывести в однострочном редакторе текст «Ошибка».


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


3. Модифицируйте созданный калькулятор для возможности изменения знака числа на обратный (кнопка « +/– »).


4. Модифицируйте созданный калькулятор так, чтобы можно было удалять последний символ (кнопка «Del»), вводить два нуля (кнопка «00») и вводить число Pi (Кнопка «Pi»).


5. Модифицируйте созданный калькулятор так, чтобы была возможность разделения групп разрядов с сохранением всей функциональности калькулятора. Например, чтобы вместо 3456190 было 3 456 190.


6. Модифицируйте созданный калькулятор так, чтобы в нём была возможность запоминать введенное число (кнопка «W») и отображать его (кнопка «R»). Причём на форме должна находиться метка, где будет отображено сохраненное число.


7. Модифицируйте созданный калькулятор так, чтобы он возводил число в квадрат (кнопка «x^2») в куб (кнопка «x^3») и в любую степень (кнопка «x^y»).


8. Модифицируйте созданный калькулятор так, чтобы он вычислял sin x, cos x, tan x и cotan x. Причём на форме должны быть два компонента RadioButton, в которых бы происходило переключение между размерностью вычисления (радианы либо градусы).


9. Модифицируйте созданный калькулятор так, чтобы числа можно было вводить прямо в окне однострочного редактора, а также с помощью клавиш + – * / выполнять соответствующие операции. Кнопка «=» должна соответствовать клавише Enter.


Задания II уровня сложности

1. Модифицируйте созданное приложение для работы с двоичными числами (перевод из двоичной системы счисления в десятичную систему и, наоборот, сложение двоичных чисел, операции Not, And, Or, Xor). Переключение между десятичной и двоичной системами счисления осуществляется через компоненты RadioButton.


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

  • вывод количества символов в массиве;
  • вычисление среднего арифметического;
  • вычисление суммы всех чисел;
  • нахождение максимального элемента массива;
  • нахождение минимального элемента массива.