Теперь приступим к рассмотрению реализации этого приложения. В табл. 3.1 приводятся сведения по настройке элементов управления, не являющихся рамками или статическими надписями.
Таблица 3.1. Параметры элементов управления формы, показанной на рис. 3.1 В листинге 3.8 приводятся объявления переменных (членов класса TForml) и методов, добавленных вручную.Листинг 3.8. Форма для измерения пробега указателя
type
TForm1 = class(TForm)
…
private
isUpdating: Boolean; //Если равен False, то показания
//в txtDistance
//не обновляются
lastPos: TPoint; //Координаты указателя во время
//прошлого замера
distance: Real; //Пройденное расстояние в пикселах
procedure StartUpdating();
procedure StopUpdating();
procedure ShowDistance();
end;
Суммарное расстояние в пикселах, пройденное указателем, сохраняется в переменной distance. Рассмотрим, как осуществляется перевод этого расстояния в метры (листинг 3.9).
Листинг 3.9. Перевод расстояния в метры с учетом масштаба
procedure TForm1.ShowDistance();
var
scale: Real;
distanceMetters: Real;
begin
//Пересчитываем текущий пробег в метры и показываем его
//в текстовом поле
//..определяем масштаб для перевода измерений в метры
scale := 0.001 * StrToInt(txtWidth.Text) / Shape1.Width;
//..подсчитываем расстояние с учетом масштаба
distanceMetters := scale * distance;
//..округляем до трех знаков (мм) и показываем
distanceMetters := Int(distanceMetters * 1000) * 0.001;
txtDistance.Text := FloatToStr(distanceMetters);
end;
В приведенном расчете нет ничего сложного, как, собственно, нет ничего сложного и во всем примере. Главная процедура приложения – обработчик для таймера Timerl. Таймер срабатывает с максимальной для него частотой (не 1 мс, конечно, но где-то 18 раз в секунду). Текст обработчикаТ1тег1Т1тег приводится в листинге 3.10.
Листинг 3.10. Подсчет разницы между положениями указателя мыши
procedure TForm1.Timer1Timer(Sender: TObject);
var
curPos: TPoint;
delta: Real;
begin
if (curPos.X <> lastPos.X) or (curPos.Y <> lastPos.Y) then
begin
GetCursorPos(curPos);
//Вычисляем разницу между текущим и прошлым
//положением мыши
delta := Sqrt(Sqr(curPos.X – lastPos.X) + Sqr(curPos.Y -
lastPos.Y));
distance := distance + delta;
//Не забываем сохранить новые координаты указателя
lastPos := curPos;
if isUpdating then
begin
//Обновим показания в текстовом поле
ShowDistance();
end;
end;
end;
Как можно увидеть при внимательном рассмотрении листинга 3.10, обновление показаний происходит при истинном значении переменной isUpdating. Значение этой переменной устанавливается в False во время задания масштаба, чтобы во время ввода значений в текстовые поля не выводились неправильные цифры (листинг 3.11).
Листинг 3.11.
Активизация/деактивизация режима ввода масштаба
procedure TForm1.cmbScaleClick(Sender: TObject);
begin
if cmbScale.Caption = \'Изменить масштаб\' then
begin
//Начинаем изменение масштаба
StopUpdating();
cmbScale.Caption := \'Принять масштаб\
txtWidth.Enabled := True;
end
else
begin
//Заканчиваем изменение масштаба
txtWidth.Enabled := False;
cmbScale.Caption := \'Изменить масштаб\
StartUpdating();
end;
end;
Процедуры StartUpdating и StopUpdating скрывают действия, которые необходимо произвести для остановки или возобновления отображения пройденного расстояния в текстовом поле. В нашем примере они выглядят крайне просто (листинг 3.12).
Листинг 3.12.
Включение/выключение обновления результатов измерения
procedure TForm1.StartUpdating();
begin
//Включаем обновление показаний в текстовом поле
isUpdating := True;
end;
procedure TForm1.StopUpdating();
begin
//Отключаем обновление показаний в текстовом поле
isUpdating := False;
end;
В завершение остается реализовать код инициализации при запуске программы и обработчик события Click для кнопки cmbClear (листинг 3.13).
Листинг 3.13.
Инициализация при запуске и код сброса счетчика
procedure TForm1.FormCreate(Sender: TObject);
begin
//Инициализируем координаты мыши
GetCursorPos(lastPos);
StartUpdating();
end;
procedure TForm1.cmbClearClick(Sender: TObject);
begin
//Сбрасываем счетчик пройденного расстояния
distance := 0;
GetCursorPos(lastPos); //Начинаем отсчет с текущей
//позиции указателя
ShowDistance();
end;
Вот, собственно, и все, что нужно для работы рассматриваемой программы. Остается лишь уточнить, что способ установки масштаба, используемый в программе, предназначен для таких разрешений мониторов, при которых нет искажений по горизонтали или вертикали. Чаще всего это такие разрешения, при которых размеры изображения по горизонтали и вертикали подчиняются пропорции 4:3 (640 х 480, 800 х 600 и т. д.). При этом такими же пропорциями должен обладать экран монитора.
Подсвечивание элементов управления
В завершение рассмотрим несложный, но достаточно полезный пример, позволяющий сделать более «живым» интерфейс приложения: изменение внешнего вида элементов управления при наведении на них указателя мыши.
В листинге 3.14 показано, как можно сделать статическую надпись похожей на гиперссылку (для большего эффекта для такой надписи можно установить свойство Cursor равным crHandPoint на этапе проектирования формы).
Листинг 3.14.
Подчеркивание и изменение цвета надписи
procedure TForm1.lblUnderlineMouseEnter(Sender: TObject);
begin
lblUnderline.Font.Style := [fsUnderline];
lblUnderline.Font.Color := RGB(0, 0, 255);
end;
procedure TForm1.lblUnderlineMouseLeave(Sender: TObject);
begin
lblUnderline.Font.Style := [];
lblUnderline.Font.Color := RGB(0, 0, 0);
end;
Осталось добавить обработчик события Click для надписи, и получится довольно правдоподобная гиперссылка, правда, выполнять она может любое действие.
Начертание шрифта можно также изменить для стандартной кнопки. Как это можно сделать, показано в листинге 3.15.
Листинг 3.15.
Изменение начертания шрифта
procedure TForm1.cmbItalicBoldMouseMove(Sender: TObject;
Shift: TShiftState; X, Y: Integer);
begin
cmbItalicBold.Font.Style := [fsItalic, fsBold];
end;
procedure TForm1.lblItalicMouseEnter(Sender: TObject);
begin
lblItalic.Font.Style := [fsItalic];
end;
В листинге 3.15 используется обработчик MouseMove для кнопки потому, что, к великому сожалению, обработчики co6biTHftMouseEnter nMouseLeave для нее (по крайней мере, с вкладки Standard) не предусмотрены.
3.2. Клавиатура
Клавиатура является основным средством для ввода информации в компьютер, поэтому не будем обходить стороной и рассмотрим некоторые не так часто используемые или не такие очевидные возможности работы с ней.
Определение информации о клавиатуре
Начнем с небольшого примера, позволяющего определить некоторую информацию о клавиатуре (листинг 3.16). Пример основан на использовании API-функции GetKeyboardType.
Листинг 3.16.
Определение информации о клавиатуре
procedure TForm1.FormCreate(Sender: TObject);
begin
//Определяем тип клавиатуры
case GetKeyboardType(0) of
1: txtType.Text := \'PC/XT или совместимая (83 клавиши)\
2: txtType.Text := \'Olivetti «ICO» (102 клавиши)\
3: txtType.Text := \'PC/AT (84 клавиши) или похожая\
4: txtType.Text := \'Расширенная (101 или 102 клавиши)\
5: txtType.Text := \'Nokia 1050 или похожая\
6: txtType.Text := \'Nokia 9140 или похожая\
7: txtType.Text := \'японская\
end;
//Определяем код типа производителя
txtSubtype.Text := IntToStr(GetKeyboardType(1));
//Определяем количество функциональных клавиш
txtKeys.Text := IntToStr(GetKeyboardType(2));
end;
При создании формы происходит заполнение текстовых полей информацией о типе клавиатуры, коде типа, присвоенном производителем, и количестве функциональных клавиш.
Пример возможного результата определения информации о клавиатуре приводится на рис. 3.2.
Рис. 3.2. Информация о клавиатуре
Опрос клавиатуры
Существует достаточно удобная альтернатива обработке событий клавиатурного ввода, которая может оказаться особенно полезной, когда нужно знать состояние сразу нескольких клавиш.
В листинге 3.17 приводится пример обработчика события TimerlTimer, определяющего, нажаты ли клавиши ↑, ↓, ←, →, а также пробел, Enter, Ctrl (правый) и Alt (правый).
Листинг 3.17.
Определение состояния некоторых клавиш
procedure TForm1.Timer1Timer(Sender: TObject);
var
buttons: TKeyBoardstate;
begin
//Получаем состояния клавиш
GetKeyboardState(buttons);
//Отобразим состояния клавиш
//..пробел
if buttons[VK_SPACE] and 128 <> 0 then
SendMessage(cmbSpace.Handle, BM_SETSTATE, BST_CHECKED, 0)