четверг, 26 мая 2011 г.

Урок 10 . Компонент - сообщение (TMyMessenger)


Привет.
С Вами Веселов Александр.
В предыдущем уроке мы познакомились подробно с процессами создания, компиляции, установки и удаления компонента собственного производства.
Прежде, чем мы продолжим изучение процесса  создания собственных компонентов, я хочу на простом наглядном примере показать работу стандартных компонентов, указав на тот недостаток, наличие которого побудило меня к написанию собственного инструмента.
Создайте проект VCL  приложения, разместите на форме пару кнопок и под обработчики событий каждой положите процедуры, описанные ниже:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;

// Объявление константы перевода каретки
const CR=#13#10;

type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
Showmessage('В заголовке Showmessage - имя приложения');
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
MessageDlg('Файл ini не определён. Обратитесь к разработчику.'+CR+'Иллюстрация сообщения об ошибке',mtError, [mbOk], 0);
If MessageDlg('Файл ini не определён.'+CR+'Желаете обратиться к разработчику?'+CR+'Иллюстрация сообщения с подтверждением выбора',mtConfirmation, [mbYes]+[mbNo], 0, mbNo)=6
then
Showmessage('Была нажата кнопка Yes')
else
Showmessage('Была нажата кнопка No')
;
end;

end.
Попутно я объявил очень полезную константу CR, чтобы применить ее при оформлении выводимого на экран сообщения.
Первая кнопка вызывает на экран процедуру Showmessage, вторая - подряд два вызова функции MessageDlg. Установите курсор на заголовок процедуры и нажмите F1, чтобы вызвать справочную систему Delphi For Win 32, которая выведет на экран окно с текстом следующего содержания:







Переведу:
в первой строке - заголовок: ShowMessage
Краткое описание: Показывает сообщение с кнопкой “ОК”.
Данная процедура расположена в модуле: Dialogs.
Синтаксис:
[Delphi] - для среды (справка единая на все продукты производителя) описание заголовка процедуры со всеми параметрами и т.п.
Description - подробное описание работы процедуры, где сказано буквально следующее: вызов ShowMessage отображает простое сообщение с кнопкой “ОК”. Имя файла исполняемого приложения отображается как подпись (я бы сказал - заголовок) сообщения.
Параметр Msg - строка, которая выводится в сообщении.
Ниже по тексту справки написано о том, что данную строку можно форматировать и т.п. и далее приведены ссылки “смотреть также”, из которых становится ясно, что вариантов у нас не так много.

Щелкнув по одной из ссылок - по MessageDlg - давайте познакомимся с возможностями еще одного инструмента, использованного в нашем примере. Это - функция, которая позволяет вывести на экран сообщение, указать тип сообщения, определить набор отображаемых в сообщении кнопок, в другом варианте - задать координаты, чтобы сообщение выводилось не по центру экрана, определить контекстную справку и даже задать кнопку из набора, которая будет иметь фокус по умолчанию. Кроме того, в постобработке всегда можно вычислить, какая же кнопка была нажата (см пример), поскольку функция возвращает целое значение соответствующей константы. Вот, казалось бы, какие богатые возможности, но... В заголовке сообщения эта функция выводит английские слова, сопоставленные разработчиками выбранному типу сообщения. Опять получается не совсем хорошо... Я уже просил моего читателя, если он пишет программы не для собственных нужд, постараться не пугать своего будущего пользователя латиницей.
Напрашивается вывод: хочешь иметь в заголовке сообщения любую фразу, написанную на родном языке - применяй изобретательность.
Мне ничего не остается делать, как привести ниже текст модуля нового компонента с подробными комментариями. А в наглядном видео уроке, который Вы можете посмотреть или скачать, проиллюстрировать не только процесс создания компонента TMyMessenger, но и особенности его использования.
Содержимое файла MyMsgDlgWithTitleLes.pas:
unit MyMsgDlgWithTitleLes;
interface
uses Classes
, Dialogs
, Forms
;

type
TMyMessengerLes = class(TComponent)                 // Объявление нового класса
private                                             // В этой секции заданы поля, в которых будут храниться значения свойств
FMessageString: String;                           // Собственно строка сообщения
FTitleString: String;                             // Заголовок сообщения
FMessageType: TMsgDlgType;                        // Тип сообщения
FButtons: TMsgDlgButtons;                         // Набор кнопок
protected                                           // Здесь объявлена знакомая по справочной системе функция
function MessageDlg(Title : string; const Msg: string; DlgType: TMsgDlgType; Buttons: TMsgDlgButtons; HelpCtx: Longint): Integer;
public
Function ShowMessage: Integer;                    // Определение метода компонента:
// это функция показа на экране сообщения (NB: название просто совпало с названием стандртной процедуры)
constructor Create( AOwner : TComponent );  override; // Объявление конструктора компонента
published                                           // В этой секции объявлены свойства будущего компонента, которые будут отображаться в Инспекторе объектов
Property MessageString:String read FMessageString write FMessageString; //
Property TitleString:String read FTitleString write FTitleString;
Property MessageType:TMsgDlgType read FMessageType write  FMessageType;
Property Buttons: TMsgDlgButtons read FButtons write FButtons;
end;

procedure Register;
implementation
procedure Register;                                    // Процедура регистрации компонента
begin
RegisterComponents('Lessons', [TMyMessengerLes]);    // Компонент TMyMessengerLes будет зарегистрирован на вкладке Lessons
end;

constructor TMyMessengerLes.Create( AOwner : TComponent );    // В параметрах, передаваемых в конструктор компонента объявляется,
// что создаваемый визуальный компонент наследуется от TComponent
begin
inherited Create( AOwner );                                 // Оператор, выполняющий процесс присвоения всех свойств родителя новому компоненту
Buttons:=[mbOK];                                            // Пусть для примера в свойстве, определяющем набор кнопок, будет что-то вписано
MessageType:=mtInformation;                                 // Тип нового, располагаемого на форме компонента изначально будет Information
end;

Function   TMyMessengerLes.ShowMessage: Integer;                 // Описание функции, отрабатывающей метод компонента ShowMessage
begin
Result:=MessageDlg(FTitleString, FMessageString, MessageType, Buttons, 0); // Функция возвращает результат вызова стандартной функции
// с параметрами, извлеченными из полей (мест хранения),
// хранящих свойства компонента

end;
// Переопределение стандартной функции
function TMyMessengerLes.MessageDlg(Title: string; const Msg: string; DlgType: TMsgDlgType; Buttons: TMsgDlgButtons; HelpCtx: Integer): Integer;
begin
with CreateMessageDialog(Msg, DlgType, Buttons) do         // В момент создания диалога
try
Caption     := Title;                                  // Изменяется заголовок (подпись) окна
HelpContext := HelpCtx;
HelpFile    := '';
Position := poScreenCenter;
Result := ShowModal;
finally
Free;
end;
end;

end.
Что же дальше?
создадим еще один несложный, но полезный компонент - гиперссылку, и уже совсем скоро - более сложный компонент с использованием формы "О программе".


вторник, 17 мая 2011 г.

Урок 9 . Создание собственного компонента (TMyLabel)



Я предлагаю на время отложить проект таймера, которому были посвящены предыдущие уроки, вплоть до последнего. Когда-то я говорил, что использование стандартных компонентов рано или поздно надоест, если, конечно, Вы программируете постоянно, а не увлеклись этим в связи с предстоящим завтра зачетом. “Почему?” - удивленно спросите Вы. В качестве иллюстрации ответа, приведу в пример компонент TPanel. Сколько бы я ни использовал его, мне всегда приходилось стирать вручную надписи Panel1, Panel2 и т.п., располагаемые в центре панели поумолчнию.
Возьмите следующим TLabel. Киньте его на форму. По умолчанию его свойство AutoSize установлено в True, что приводит к автоматическому изменению ширины надписи в зависимости от длины текста, помещенного в Caption. Случаев, когда от такого поведения компонента я пришел бы в восторг, что-то припомнить не могу. Наоборот, всегда приходилось переназначать это свойство вручную.
Это были первые - самые простые примеры. С них и предлагаю начать, чтобы понять процесс, а впереди будут компоненты более сложные и очень сложные.
В двух словах коснусь того, что компоненты наследуются один от другого и имеют общих предков, которых в итоге меньше, чем пальцев на одной руке, но этот материал подробно изложен во множестве учебников. К сожалению, хороших учебников мало, а те, что общепризнаны удачными (Рея Конопки и Валерия Фаронова), довольно сложны для начала. Памятуя о тех трудностях, которые испытывал я, взявшись за изучение этой темы когда-то, я и хочу в простой и доступной форме рассказать о процессе создания и отладки компонентов.
Откройте среду разработки Delphi и создайте новый компонент, выбрав в меню Component - New VCL Component (создавать мы будем компонент для библиотеки визуальных компонентов - VCL).

В следующем окне выберите TLabel в левой колонке. Это и будет предок, от которого наш компонент унаследует все свои свойства. Нажмите “Next”.
Затем придумайте имя Вашему классу, помня о том, что можно использовать только латинские буквы и все подобные имена начинаются с заглавной “Т”. Выберите или задайте имя закладки на панели инструментов и укажите каталог, где будет храниться файл. Последнее поле “Search Path” оставьте пока пустым.

<Next> - <Finish> и оболочка создаст заготовку под Ваш новый компонент:
Сохраните файл, указав выбранный на предыдущем шаге каталог.
unit MyLabelLes;                     // Заголовок модуля
interface                                 // Начало раздела Interface
uses
SysUtils, Classes, Controls, StdCtrls;   // Объявления задействованных модулей
type
TMyLabelLes = class(TLabel)  // Объявление класса, наследуемого от TLabel
private
{ Private declarations }
protected
{ Protected declarations }
public
{ Public declarations }
published
{ Published declarations }
end;
procedure Register;                  // Объявление стандартной процедуры,
// регистрирующей новый компонент
implementation                         // Начало раздела  implementation
procedure Register;                 // Код стандартной процедуры
begin
RegisterComponents('Lessons', [TMyLabelLes]);  // Оператор, который зарегистрирует
// компонент TMyLabelLes на закладке
// Lessons
end;
end.
Все остальное нам придется дописать ручками.
А всего остального осталось не много: дописать в раздел implementation конструктор - специальную процедуру:
constructor TMyLabelLes.Create( AOwner: TComponent );  // Прародитель - TComponent
begin
inherited Create(AOwner);                       // Наследуются все “прелести” прародителя
AutoSize:=False;                                     // А следующие три оператора... устанавливают
Height:=13;                                          // нужные нам свойства, которыми будет
Width:=80;                                          // наделен новый экземпляр компонента
// сразу после его создания
end;
Эта специальная процедура “производит на свет”, как Вы могли догадаться из названия, собственно сам компонент. Помните, я упоминал в начале урока, что таких прародителей - по пальцам перечесть. Вот один из них: в первой строчке виден - TComponent. От него происходят визуальные компоненты.
Следующий оператор передает все свойства прародителя в новый компонент. Если больше ничего не написать, то мы получим новый компонент TMyLabelLes в точности копирующий обычную метку TLabel. Но у нас задача - сделать свойство AutoSize=False, чтобы надписи, размещаемые на форме, не меняли свою ширину в зависимости от помещенного в них текста. Следующие три оператора принудительно изменяют свойства у нового компонента.
Не забудьте добавить объявление этой процедуры в раздел Public
Constructor Create( AOwner: TComponent); override;
Сохраните файл.
Делаем следующий шаг - создаем пакет Меню - File - New … как показано на рисунке:

Затем кнопкой “Add File To Project” (на панели кнопок) необходимо добавить созданный на предыдущем шаге файл MyLabelLes.pas в данный пакет.
Обязательно сохраните пакет, дав ему осмысленное имя. Причем, если Вы попытаетесь назвать его так же, как файл, содержащий текст компонента, то Delphi выдаст ошибку, поэтому назовите его, например: MyLabelLesPack.
Затем скомпилируйте пакет, щелкнув правой кнопкой в окне Project Manager на заголовке проекта и выбрав в контекстном меню Compile.
Если система определит наличие ошибок, исправьте их и повторите компиляцию.
В случае, если ошибок нет, можно приступать к установке компонента в Вашу среду разработки. Для этого щелкните правой кнопкой все по тому же заголовку проекта в окне Project Manager и выберите Install.
Система должна выдать сообщение об успешной установке.
Важно: нажмите пиктограмму сохранить на панели кнопок.
Закройте проект.
Полюбуйтесь своим детищем:
меню Component - Install Packages, найдите его в списке и щелкните кнопку Components:

Есть!
Теперь нужно протестировать новый компонент.
Для чего создайте новый проект VCL Form Application и положите на форму Label с вкладки Standart и MyLabelLes с вкладки Lessons. Обратите внимание на разницу свойств и поведения этих компонентов. Это особенно видно в наглядном видео уроке.
Разберемся, где что хранится. Для чего это нужно? Предположим, что Вы отлаживали какой-то компонент, долго его настраивали и, наконец, получили то, что хотели. Но для его отладки Вы использовали временную папку, а все компоненты у вас живут где-то в Program Files/Borland и т.д. Или, например, Вы хотите передать этот удачный компонент коллеге по работе. Или у вас два компьютера: настольный и переносной. А Вы хотите везде иметь среду разработки с одинаковым набором. Чтобы лучше это понять, давайте удалим вновь созданный компонент. Перенесем его в другую папку и вновь инсталлируем его.
Я сделаю это один раз, в этом уроке и больше к этому важному вопросу возвращаться не будем.
Итак.
1. Закройте все открытые проекты (File - Close All)
2. Меню, Component и т.д., как показано на рисунке:

3. Очень важный шаг: удалите путь, меню Tools - Delphi Option - Library - Win 32 и т.д., как показано на рисунке.

Если этого не сделать, то перенос компонента в другую папку приведет к некоторой путанице. Система может упрямо тащить компонент из прежнего места.
4. Проверьте, используя проводник, каталог, указанный как Output:

В этой папке не должно остаться никаких файлов с тильдами в имени файла, никаких MyLabelLesPack.bpl.
5. Перенесите папку с исходниками компонента в папку Program Files/borland и т.д. или любой другой, удобный Вам.
6. Повторите установку компонента.
Что же дальше?
Предлагаю продолжить разговор о создании нескольких собственных компонентов: я к ним привык, а для Вас - полезная практика. Впереди - разговор о создании компонента сообщения, компонента гиперссылки и более сложного компонента - формы “About”. А чуть позже мы вернемся к оставленной временно программе и используем в ней новые конструкции.

четверг, 12 мая 2011 г.

Урок 8 . Учим таймер “петь”


В предыдущем уроке основной алгоритм работы программы бытового таймера был реализован и отлажен: таймер может производить обратный отсчет, показывая значения убывающих секунд и останавливаться по окончании отсчета.
Но этого, очевидно, не достаточно. было бы не плохо, если бы по окончании отсчета, уснувшему на клавиатуре от непосильного труда пользователю подавался звуковой сигнал.
Чтобы поместить на форму компонент TMediaPlayer, нужно отыскать его на вкладке “System” панели инструментов и перетащить в подготовленное для него место.
Прежде, чем писать какой-то код для работы плеера, настройте его внешний вид, отключив “лишние” кнопки: вряд ли в процессе отладки понадобятся кнопки записи, извлечения диска, перемотки, да и паузы тоже. Оставьте видимыми только кнопки btPlay и btStop.
Сам плеер в момент сразу после старта программы нам тоже на форме ни к чему, поэтому после того, как все будет отлажено, установите его свойства Enabled и  Visible в False
Теперь “начнем с конца” и позаботимся об остановке плеера, добавив в процедуру
Do_StopExecute строчку:
// Остановка плеера
MediaPlayer1.Stop;
Какой же звук будет воспроизводить наш плеер? Добавьте в папку с проектом любой небольшой звуковой файл (wav), я его назвал MyTimer.wav.
Организуйте новую секцию “Sound” в файле настроек MyTimer.ini:
[Sound]
SoundFullPath=F:\! Мои программы\MyTimer\MyTimer.wav
Замечу, что путь к программе, а соответственно и к Вашему файлу wav будет отличаться от того пути, что указан выше: F:\! Мои программы\MyTimer\.
Добавьте в секцию “interface” в переменные:
MediaFileName: String;                        //  Полный путь к звуковому файлу
Затем, добавьте в процедуру FormCreate строку, считывающую информацию из файла настроек ini, из секции “Sound”:
MediaFileName:=Ini.ReadString('Sound','SoundFullPath','MyTimer.wav');
и далее в этой же процедуре выполните проверку:
//  Если в ini не указан медиа файл, то попытаться воспроизвести указанный по умолчанию MyTimer.wav
If FileExists(MediaFileName)
then
begin
MediaPlayer1.FileName:=MediaFileName;
MediaPlayer1.Open;
StatusBar1.Panels[0].Text:='Звук: '+MediaFileName;
end
else
StatusBar1.Panels[0].Text:='Звуковой файл отсутствует'; // На самом деле в этом месте
// алгоритма заложена бага: если
// звукового файла нет, то и таймер           //запускать незачем, но решение этой проблемы - домашнее задание :-)
;
Для запуска плеера по событию окончания работы первого таймера впишите в соответствующую процедуру следующую строчку:
// Воспроизвести звук
MediaPlayer1.Play;
и заремить или стереть строки, которые были написаны ранее:
// Вернуть элементы управления в стартовое состояние
//  SetRecBtnStatus(1);
//  SetBtnStatus;
чтобы оставалась доступной кнопка “Стоп”.
А теперь нужно разобраться с работой медиа плеера. Для этого скомпилируем проект и выполним программу. Нажмите кнопку старт, дайте возможность таймеру закончить отсчет и... Вы услышите звук. Но, к сожалению, звук будет воспроизведен один раз. Для бытового таймера это допустимо, но не очень здорово. Пользователь отошел на минуточку и прошляпил... “А у вас молоко убежало...” (Карлсон). Нужно заставить плеер играть, пока пользователь не остановит его сам, как это реализовано в поломавшейся дешевой иностранной подделке, все еще примагниченной к дверце холодильника
Здесь я позволю себе процитировать учебник:
“В компоненте MediaPlayer определено событие OnNotify.
Событие OnNotify происходит после возвращения очередного метода, если свойство медиа-плеера Notify было установлено в true. Способ возврата любого метода медиа-плеера определяется свойством Wait. Если установить Wait равным false, то возвращение управления в приложение происходит сразу после вызова метода, не дожидаясь завершения его выполнения. Таким образом, задав Notify равным true и Wait равным false, можно обеспечить немедленный возврат в приложение и отображение пользователю текущего состояния объекта мультимедиа.
Свойства Notify и Wait действуют только на один очередной метод. Поэтому их значения надо каждый раз восстанавливать в обработчиках событий OnClick или OnNotify”
И вряд ли Вы.найдете еще что-то большее. Собственно, ради того, чтобы показать как, я и затеял этот урок. Создайте процедуру, соответствующую событию onNotify:
procedure TForm1.MediaPlayer1Notify(Sender: TObject);
begin
With Sender as TMediaPlayer do
begin
wait:=False;
case Mode of                      // анализ состояния плеера
mpPlaying:
begin
play;                                // Продолжается проигрывание
Notify:=True;
end;
mpStopped:
begin
Notify:=True;                   // Проигрывание прекращается
end
end;
end;
end;
Теперь проигрывание выбранного звука продолжается, пока пользователь не проснется и не нажмет кнопку стоп.
И наконец, создайте процедуру FormClose и запишите в нее строку закрытия плеера:
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
MediaPlayer1.Close;
end;
По поводу использования компонента TMediaPlayer я предвижу массовые возражения со стороны других программистов, мол, компонент устарел, нынче модно использовать PlaySound...
Я не зря привел цитату из учебника, подозреваю, что подобное мнение сформировалось под воздействием тех, кто не смог или не захотел разобраться.
Хорошо, расскажу, как ту же задачу решить с playsound():
1. Добавьте в Uses mmsystem;
2. В процедуре procedure TForm1.Timer1Timer(Sender: TObject);
// Воспроизвести звук
//  MediaPlayer1.Play;
PlaySound(PAnsiChar(MediaFileName), 0, SND_ASYNC or SND_LOOP);
3. В процедуре
procedure TForm1.Do_StopExecute(Sender: TObject);
begin
...
// Остановка плеера
//    MediaPlayer1.Stop;
PlaySound(0, 0, SND_ASYNC or SND_LOOP);
4. Процедура MediaPlayer1Notify(Sender: TObject) должна быть исключена.
На вид - проще, но на одну библиотеку больше... Какой выбрать способ - решать Вам.
Наглядный видео урок Вы можете посмотреть или скачать.
Что же дальше?
А дальше я Вам скажу следующее: на странице сайта “Таймер для домашнего хозяйства” Вы можете скачать скомпилированную готовую программу и пользоваться ей, несмотнря на все ее недостатки. Почему я не выложил исходные тексты и весь проект, как Вы думаете? Из жадности? Нет. Потому что в проекте помимо компонентов сторонних производителей я использую собственные компоненты, которых в Вашей среде разработки заведомо нет. Попытка открытия проекта вызвала бы ошибку, а Вы посетовали бы в мой адрес нехорошими словами... Я и сам не один десяток раз натыкался на подобные ситуации: качнешь из сети исходники, чтобы побыстрее какой-то вопрос решить, а не компилируются. И укоренилось мнение в голове: чем разбираться в чужом творчестве, проще и по времени выгоднее написать свое. Поэтому я планирую на следующем уроке подробно остановиться на процессе создания своего собственного компонента. Пусть это будет самый простой компонент - метка (Label), но я постараюсь до самых мелких подробностей проследить все детали процесса.

вторник, 3 мая 2011 г.

Урок 7. Как запомнить настройки (файл ini)

В предыдущем уроке для отладки работы таймера в качестве стартового значения я использовал величину 3 секунды. А еще раньше — приготовил несколько кнопок, которым что-то планировал назначать в момент старта программы. Вот и встает вопрос: где же хранить стартовые значения и настройки (надписи и т.п. для кнопок), сделанные оператором в ходе эксплуатации программы?


Как и у любой другой задачи, у задачи выбора места хранения пользовательских настроек и способа доступа к ним, существует несколько решений:


Обычный текстовый файл,
Текстовый файл ini,
Реестр windows,
и др...


Хранить настройки в обычном текстовом файле - самый простой, но не очень удобный способ. Неудобства вытекают из неудобства процессов записи и считывания данных из файла (об этом Вы можете прочитать в учебниках).


Хранить какие-то записи в реестре Windows не так уж сложно и мудрено, как может показаться на первый взгляд, но! Для установки программы в компьютер и удаления ее, нужно будет написать специальную программу — инсталлятор, которая правильно обработает реестр. На данном этапе мы себе такой задачи не ставим.


А посему — воспользуемся золотой серединой — файлом ini.


Для того, чтобы программа могла работать с такими файлами, необходимо в разделе interface в uses в дополнение имеющегося списка дать ссылку на соответствующую библиотеку:


Uses iniFiles;

в разделе объявления глобальных переменных объявить переменную:
ini: TIniFile;


а в каталоге программы средствами операционной системы создать текстовый файл


MyTimer.ini, в который вписать (можно скопировать из этого текста) следующее содержание:


[General]
StartMin=1
StartSec=30


[FixedBtns]
B1Capt=Яйцо всмятку
B1Min=2
B1Sec=25
B1Hnt=Яйцо всмятку
B2Capt=
B2Min=0
B2Sec=0
B2Hnt=Подсказка не указана
B3Capt=
B3Min=0
B3Sec=0
B3Hnt=Подсказка не указана
B4Capt=
B4Min=0
B4Sec=0
B4Hnt=Подсказка не указана
B5Capt=-
B5Min=0
B5Sec=0
B5Hnt=Подсказка не указана
B6Capt=-
B6Min=0
B6Sec=0
B6Hnt=Подсказка не указана


В первой секции [General] задаются стартовые значения минут и секунд. А в следующей секции [FixedBtns] для шести подготовленных нами «быстрых» кнопок заданы последовательно:


Подпись,
Минуты,
Секунды,
Текст всплывающей подсказки (Hint).


Не забудьте сохранить созданный файл.
В какой момент нужно обратиться к нему? Форма в процессе своего создания проходит стадии, точнее: в процессе создания формы возникают события создания (Create), показа (Show) и активации (Activate). В чем принципиальная разница каждого из этих моментов, Вы легко узнаете из учебников. Я же по секрету Вам скажу, что все, что я напишу в обработчике события создания формы будет работать и в двух других случаях.


А напишу я там вот этот фрагмент текста:


procedure TForm1.FormCreate(Sender: TObject);
begin


// Подготовить к работе файл ini, расположенный в текущем каталоге
ini:=TiniFile.Create(GetCurrentDir + '\MyTimer.ini');


// Установить стартовые значения цифровым полям
Minutes.Value:=Ini.ReadInteger('General','StartMin',1);
Secunds.Value:=Ini.ReadInteger('General','StartSec',30);


// Назначить интервал срабатывания главного таймера
Timer1.Interval:=CalcInterval();


// Установить статус элементов управления формы (1 — статус после старта программы, т.е. Таймер не запущен)
SetRecBtnStatus(1);
SetBtnStatus;


// Настроить фиксированные кнопки
FixedBtns.B1_Caption:=Ini.ReadString('FixedBtns','B1Capt','-');
FixedBtns.B1_Min:=Ini.ReadInteger('FixedBtns','B1Min',0);
FixedBtns.B1_Sec:=Ini.ReadInteger('FixedBtns','B1Sec',0);
FixedBtns.B1_Hint:=Ini.ReadString('FixedBtns','B1Hnt','Подсказка не указана');
FixedBtns.B2_Caption:=Ini.ReadString('FixedBtns','B2Capt','-');
FixedBtns.B2_Min:=Ini.ReadInteger('FixedBtns','B2Min',0);
FixedBtns.B2_Sec:=Ini.ReadInteger('FixedBtns','B2Sec',0);
FixedBtns.B2_Hint:=Ini.ReadString('FixedBtns','B2Hnt','Подсказка не указана');
FixedBtns.B3_Caption:=Ini.ReadString('FixedBtns','B3Capt','-');
FixedBtns.B3_Min:=Ini.ReadInteger('FixedBtns','B3Min',0);
FixedBtns.B3_Sec:=Ini.ReadInteger('FixedBtns','B3Sec',0);
FixedBtns.B3_Hint:=Ini.ReadString('FixedBtns','B3Hnt','Подсказка не указана');
FixedBtns.B4_Caption:=Ini.ReadString('FixedBtns','B4Capt','-');
FixedBtns.B4_Min:=Ini.ReadInteger('FixedBtns','B4Min',0);
FixedBtns.B4_Sec:=Ini.ReadInteger('FixedBtns','B4Sec',0);
FixedBtns.B4_Hint:=Ini.ReadString('FixedBtns','B4Hnt','Подсказка не указана');
FixedBtns.B5_Caption:=Ini.ReadString('FixedBtns','B5Capt','-');
FixedBtns.B5_Min:=Ini.ReadInteger('FixedBtns','B5Min',0);
FixedBtns.B5_Sec:=Ini.ReadInteger('FixedBtns','B5Sec',0);
FixedBtns.B5_Hint:=Ini.ReadString('FixedBtns','B5Hnt','Подсказка не указана');
FixedBtns.B6_Caption:=Ini.ReadString('FixedBtns','B6Capt','-');
FixedBtns.B6_Min:=Ini.ReadInteger('FixedBtns','B6Min',0);
FixedBtns.B6_Sec:=Ini.ReadInteger('FixedBtns','B6Sec',0);
FixedBtns.B6_Hint:=Ini.ReadString('FixedBtns','B6Hnt','Подсказка не указана');


// Освободить память
ini.Free;


// Настроить фиксированные кнопки
SetFixedBtns;


// Визуализация
SetMyLabel(); // Внести изменения :=CalcT();


end;



Для работы программы необходимо объявить еще ряд переменных и функций


Function TForm1.CalcT(): Integer;
begin


// Подсчитывает количество секунд, переводя минуты в секунды
Result:=(Minutes.Value*60+Secunds.Value);


end;


Function TForm1.CalcInterval(): Integer;
begin


// Подсчитывает интервал работы таймера в мс
Result:=(Minutes.Value*60+Secunds.Value)*1000;


end;



А чтобы все новые функции и процедуры работали, необходимо в самом начале модуля формы объявить пару новых типов — записей:


Type // Объявление типа
TBtnsStatus = record
BtStop: Boolean;
BtStart: Boolean;
N_File: Boolean;
N_Do: Boolean;
N_Service: Boolean;
N_Help: Boolean;
BtnMinPlus: Boolean;
BtnMinMinus: Boolean;
BtnSecPlus: Boolean;
BtnSecMinus: Boolean;
Btn1: Boolean;
Btn2: Boolean;
Btn3: Boolean;
Btn4: Boolean;
Btn5: Boolean;
Btn6: Boolean;
end;


Type // Объявление типа
TFixedBtns = record
B1_Caption: String;
B1_Min: Integer;
B1_Sec: Integer;
B1_Hint: String;
B2_Caption: String;
B2_Min: Integer;
B2_Sec: Integer;
B2_Hint: String;
B3_Caption: String;
B3_Min: Integer;
B3_Sec: Integer;
B3_Hint: String;
B4_Caption: String;
B4_Min: Integer;
B4_Sec: Integer;
B4_Hint: String;
B5_Caption: String;
B5_Min: Integer;
B5_Sec: Integer;
B5_Hint: String;
B6_Caption: String;
B6_Min: Integer;
B6_Sec: Integer;
B6_Hint: String;
end;,



не забыв объявить две новые переменные на основе этих типов:

BtnsStatus: TBtnsStatus;
FixedBtns: TfixedBtns;

В первой будет храниться статус элементов управления (кнопок, пунктов меню), во второй — настройки фиксированных кнопок.


Пара функций SetRecBtnStatus и SetBtnStatus всегда работают совместно:
первая — устанавливает необходимые значения переменной BtnsStatus, вторая — собственно устанавливает статусы элементов управления на основе значений этой переменной. Конструкция может на первый взгляд показаться громоздкой для такой незначительной программы, но хорошая привычка, выработанная на простом, будет только в плюс при разработке больших и сложных программ. Здесь я показываю один из приемов, а ваше дело — использовать его в дальнейшем или нет в своей практике.


Теперь, когда программа умеет отображать статус элементов управления и подсчитывать интервал работы таймера, нужно переписать процедуру запуска отсчета в следующем виде


procedure TForm1.Do_StartExecute(Sender: TObject);
var
i: integer;
begin


i:=CalcInterval();
If i=0 // Если пользователь ошибся с назначением времени отсчета,
then // то - выход
exit;


// Перевод элементов управления в режим отсчета
SetRecBtnStatus(2);
SetBtnStatus;


// Назначение интервала срабатывания основного таймера
Timer1.Interval:=i;


// Визуализация
SetMyLabel();


// Старт таймера
Timer2.Enabled:=True;
Timer1.Enabled:=True;


end;



Нужно внести изменения в процедуру остановки


procedure TForm1.Do_StopExecute(Sender: TObject);
begin


// Переводэлементов управления в исходное состояние
SetRecBtnStatus(1);
SetBtnStatus;


// Остановка таймера
Timer1.Enabled:=False;
Timer2.Enabled:=False;


// Визуализация
MyLabel4.Caption:='Таймер остановлен';


end;



Вернитесь к процедуре


procedure TForm1.Timer1Timer(Sender: TObject);
begin


// Остановить таймер
Timer1.Enabled:=False;
Timer2.Enabled:=False;


// Кнопки в положение проигрывание
SetRecBtnStatus(2);
SetBtnStatus;


// Вывод сообщения оператору
MyLabel4.Caption:='Отсчет завершен';


// Воспроизвести звук несколько раз или бесконечно


// Вернуть элементы управления в стартовое состояние
SetRecBtnStatus(1);
SetBtnStatus;


end;



и внесите в нее дополнения, так же связанные с возможностями изменять и отображать статус элементов управления.


Для работы быстрых кнопок, под событие нажатия каждой кнопки подложите вот такой код:

procedure TForm1.Button1Click(Sender: TObject);
begin
Minutes.Value:=FixedBtns.B1_Min;
Secunds.Value:=FixedBtns.B1_Sec;
SetMyLabel();
end;,


изменив, соответственно индекс (номер кнопки).


И последнее:
напишите обработчики нажатия кнопок увеличения и уменьшения количества минут и секунд по вот этому образцу:


procedure TForm1.Do_MinPlusExecute(Sender: TObject);
begin


// + минута
Minutes.Value:=Minutes.Value+1;
SetMyLabel();


end;



procedure TForm1.Do_SecMinusExecute(Sender: TObject);
begin


// - секунда
If Secunds.Value>0 // При уменьшении количества секунд,
then // ограничиться положительными значениями
begin
Secunds.Value:=Secunds.Value-1;
SetMyLabel();
end;


end;



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


Что же дальше?


Знакомство с медиа проигрывателем - научим таймер подавать голос!