В Lazarus и Free Pascal, как и в Delphi, компонент TTimer часто используется для выполнения периодических задач. Иногда возникает необходимость управлять большим количеством таймеров, что может привести к загромождению кода и его дублированию. В этой статье мы рассмотрим, как объединить несколько таймеров в один компонент, упростив структуру вашего приложения.
Проблема:
Представьте, что у вас есть приложение, где требуется реализовать несколько независимых таймеров, каждый из которых отвечает за свою задачу (например, обновление данных, анимация, проверка состояния соединения и т.д.). Использование отдельных компонентов TTimer для каждой задачи приводит к:
Раздуванию кода: Код для настройки и управления каждым таймером дублируется.
Усложнению поддержки: Изменения в логике работы таймеров необходимо вносить в нескольких местах.
Сложности масштабирования: Добавление новых таймеров требует добавления новых компонентов и соответствующего кода.
Решение:
Основная идея заключается в создании класса, который инкапсулирует в себе несколько таймеров TTimer и предоставляет удобный интерфейс для управления ими. Рассмотрим пример реализации:
unit TimerUnit;
{$mode objfpc}{$H+}
interface
uses
SysUtils, Classes, ExtCtrls;
type
// Тип процедуры-обработчика события таймера
TTimerEvent = procedure(Sender: TObject) of object;
// Класс, управляющий несколькими таймерами
TMultiTimer = class(TComponent)
private
FTimers: TList; // Список таймеров
FEnabled: Boolean; // Общее состояние таймеров
// Внутренний класс для представления таймера
TTimerInfo = class(TObject)
public
Timer: TTimer;
Interval: Integer;
OnTimerEvent: TTimerEvent;
end;
procedure TimerEvent(Sender: TObject); // Обработчик события таймера
protected
procedure SetEnabled(Value: Boolean);
virtual procedure Notification(AComponent: TComponent; Operation: TOperation); override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
// Добавление нового таймера
procedure AddTimer(Interval: Integer; OnTimer: TTimerEvent);
// Удаление таймера
procedure RemoveTimer(Index: Integer);
// Запуск всех таймеров
procedure Start;
// Остановка всех таймеров
procedure Stop;
// Свойства
property Enabled: Boolean read FEnabled write SetEnabled;
end;
implementation
{ TMultiTimer }
constructor TMultiTimer.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FTimers := TList.Create;
FEnabled := False;
end;
destructor TMultiTimer.Destroy;
var
i: Integer;
begin
Stop;
for i := 0 to FTimers.Count - 1 do
begin
TTimerInfo(FTimers[i]).Timer.Free;
TTimerInfo(FTimers[i]).Free;
end;
FTimers.Free;
inherited Destroy;
end;
procedure TMultiTimer.AddTimer(Interval: Integer; OnTimer: TTimerEvent);
var
TimerInfo: TTimerInfo;
begin
TimerInfo := TTimerInfo.Create;
TimerInfo.Timer := TTimer.Create(Self); // Важно: Owner - Self, чтобы таймер уничтожался вместе с компонентом
TimerInfo.Timer.Interval := Interval;
TimerInfo.Timer.OnTimer := TimerEvent;
TimerInfo.Interval := Interval;
TimerInfo.OnTimerEvent := OnTimer;
FTimers.Add(TimerInfo);
if FEnabled then
TimerInfo.Timer.Enabled := True;
end;
procedure TMultiTimer.RemoveTimer(Index: Integer);
var
TimerInfo: TTimerInfo;
begin
if (Index >= 0) and (Index < FTimers.Count) then
begin
TimerInfo := TTimerInfo(FTimers[Index]);
TimerInfo.Timer.Free;
TimerInfo.Free;
FTimers.Delete(Index);
end;
end;
procedure TMultiTimer.Start;
var
i: Integer;
begin
FEnabled := True;
for i := 0 to FTimers.Count - 1 do
begin
TTimerInfo(FTimers[i]).Timer.Enabled := True;
end;
end;
procedure TMultiTimer.Stop;
var
i: Integer;
begin
FEnabled := False;
for i := 0 to FTimers.Count - 1 do
begin
TTimerInfo(FTimers[i]).Timer.Enabled := False;
end;
end;
procedure TMultiTimer.TimerEvent(Sender: TObject);
var
i: Integer;
begin
for i := 0 to FTimers.Count - 1 do
begin
if TTimerInfo(FTimers[i]).Timer = Sender then
begin
TTimerInfo(FTimers[i]).OnTimerEvent(Self); // Передаем Self как Sender
Break;
end;
end;
end;
procedure TMultiTimer.SetEnabled(Value: Boolean);
begin
if FEnabled <> Value then
begin
if Value then
Start
else
Stop;
end;
end;
procedure TMultiTimer.Notification(AComponent: TComponent; Operation: TOperation);
var
i: Integer;
begin
inherited Notification(AComponent, Operation);
if (Operation = opRemove) and (AComponent = Owner) then
begin
// Освобождаем ресурсы, если владелец (форма) уничтожается
for i := 0 to FTimers.Count - 1 do
begin
TTimerInfo(FTimers[i]).Timer.Free;
TTimerInfo(FTimers[i]).Free;
end;
FTimers.Free;
end;
end;
end.
Пояснения к коду:
TMultiTimer - класс, представляющий наш компонент, управляющий несколькими таймерами. Он наследуется от TComponent, чтобы его можно было разместить на форме.
FTimers: TList - список, хранящий информацию о каждом таймере.
TTimerInfo - внутренний класс, содержащий экземпляр TTimer, интервал, и обработчик события.
AddTimer - добавляет новый таймер в список. Важно указать Self в качестве владельца таймера (TTimer.Create(Self)), чтобы таймер был автоматически уничтожен вместе с компонентом TMultiTimer.
RemoveTimer - удаляет таймер из списка.
Start и Stop - запускают и останавливают все таймеры, соответственно.
TimerEvent - общий обработчик события OnTimer для всех таймеров. Он определяет, какой именно таймер вызвал событие, и вызывает соответствующий обработчик.
SetEnabled - устанавливает общее состояние таймеров (включены или выключены).
Notification - переопределенный метод, который обрабатывает уведомления об уничтожении владельца компонента (например, формы). Это необходимо для освобождения ресурсов, выделенных для таймеров.
Пример использования:
uses
..., TimerUnit;
type
TForm1 = class(TForm)
MultiTimer1: TMultiTimer;
Label1: TLabel;
Label2: TLabel;
private
procedure Timer1Tick(Sender: TObject);
procedure Timer2Tick(Sender: TObject);
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.lfm}
procedure TForm1.Timer1Tick(Sender: TObject);
begin
Label1.Caption := DateTimeToStr(Now);
end;
procedure TForm1.Timer2Tick(Sender: TObject);
begin
Label2.Caption := IntToStr(Random(100));
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
MultiTimer1 := TMultiTimer.Create(Self);
MultiTimer1.AddTimer(1000, Timer1Tick); // Обновление времени
MultiTimer1.AddTimer(500, Timer2Tick); // Генерация случайного числа
MultiTimer1.Enabled := True; // Запускаем все таймеры
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
MultiTimer1.Free;
end;
В этом примере мы создаем экземпляр TMultiTimer на форме и добавляем два таймера: один для обновления времени в Label1, а другой для генерации случайного числа в Label2.
Альтернативное решение:
Вместо использования TList для хранения информации о таймерах, можно использовать TObjectList с указанием параметра True в конструкторе. Это позволит автоматически освобождать память, занимаемую объектами TTimerInfo при удалении их из списка. Например:
FTimers := TObjectList.Create(True); // Автоматическое освобождение объектов
В этом случае, в деструкторе TMultiTimer не нужно явно освобождать память, занимаемую объектами TTimerInfo.
Преимущества использования TMultiTimer:
Централизованное управление: Все таймеры управляются из одного места.
Уменьшение дублирования кода: Код для настройки и управления таймерами инкапсулирован в классе TMultiTimer.
Упрощение поддержки: Изменения в логике работы таймеров вносятся в одном месте.
Улучшение масштабируемости: Добавление новых таймеров требует только добавления новых записей в список таймеров.
Заключение:
Использование компонента TMultiTimer позволяет значительно упростить структуру приложений, требующих использования нескольких таймеров. Этот подход позволяет централизованно управлять таймерами, уменьшить дублирование кода и упростить поддержку и масштабирование приложения. Предложенный пример кода можно адаптировать и расширить в соответствии с потребностями вашего проекта.
В Lazarus и Free Pascal можно объединить несколько таймеров TTimer в один компонент, создав класс, который инкапсулирует таймеры и предоставляет централизованное управление ими, уменьшая дублирование кода и упрощая поддержку.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.