По просьбе читателя моего блога в данной статье подробно рассматривается алгоритм отображения прогресса запуска приложения.
В статье “Пограничная заставка” я рассказал о том, как создать форму-заставку, которая должна появляться раньше основной формы и оставаться какое-то время на экране. А в это самое время программа выполняет действия, связанные с подготовкой к работе: инициализирует переменные и массивы, загружает графические ресурсы – одним словом, готовит приложение к работе. Особенно это востребовано для сложных приложений, в которых формы создаются или модифицируются с помощью скриптов. Также этот прием используется в проектах, которые используют СУБД MySQL, так как появляется возможность выполнить инициализацию компонентов до запуска основой формы.

Форма-заставка создается скриптами. Она содержит фоновое изображение (1), название проекта с тенью (2), номер версии (3) и строку прогресса запуска (4).
Splash.pas
В проекте “Справочник разработчика” применяется модульная структура – скрипты, необходимые для выполнения определенного класса задач, хранятся в отдельных файлах – модулях. В модуле splash.pas находятся функция создания формы-заставки Splash_Create() и процедура отображения формы с информацией о прогрессе загрузки Splash_ShowProgress(), а также константы, определяющие работу формы-заставки.
Модуль Splash.pas ссылается на другие модули: AppConstVar.pas, Label.pas и License.pas, которые находятся в папке Script\VClass. То есть для использования данного модуля в вашем проекте также должны присутствовать связанные с ним модули. Кроме явных ссылок, имеются неявные зависимости от других модулей, таких как System\Utils.pas. Все исходные файлы проекта можно найти в описании программы.
Ниже приводится содержание модуля с подробными комментариями и указаниями на связанные модули
// Заставка
// 05.05.2023
// function Splash_Create:TForm; - создание формы заставки
// procedure Splash_ShowProgress( AText: string ); - отобразить прогресс загрузки
uses 'VClass\AppConstVar.pas', 'VClass\Label.pas', 'VClass\License.pas';
const
SPLASH_FORM_NAME = 'frmSplash'; // имя формы
SPLASH_DELAY_MIN = 2000; // минимальное время отображения заставки;
SPLASH_LOGO_WIDTH = 350; // Ширина изображения
SPLASH_LOGO_HEIGHT = 169; // Высота изображения
SPLASH_BORDER = 2; // толщина рамки вокруг изображения
SPLASH_BORDER_COLOR = clBlack; // цвет рамки
SPLASH_FILE_NAME = 'Images\Logo\logo_350x169.jpg'; // файл фонового изображения
SPLASH_FONT_SIZE = 28; // размер шрифта для названия
SPLASH_FONT_COLOR = clWhite; // цвет для названия
SPLASH_SHADOW_COLOR = clBlack; // цвет тени
SPLASH_VER_FONT_SIZE = 11; // размер шрифта для версии
SPLASH_VER_FONT_COLOR = clWhite; // цвет шрифта для версии
procedure Splash_ShowProgress( AText: string );
// отобразить прогресс загрузки
var
tmpLabel: TLabel;
tmpForm: TForm;
begin
// находим форму заставки по имени; такой поиск позволяет обходиться без глобальной переменной
tmpForm := GetFormByName(SPLASH_FORM_NAME); // {System\Utils.pas}
// находим на форме компонент-метку; поиск также осуществляется по имени компонента
FindC(tmpForm,'labProgress',tmpLabel); // {System\Utils.pas}
// прописываем текст прогресса в заголовок метки; пробел в конце добавляется для эстетики,
// так как содержимое метки выравнивается по правому краю.
tmpLabel.Caption := AText + ' ';
// вызываем метод, который позволяет обработать все системные сообщения;
// это необходимо для прорисовки нового содержимого заголовка метки
Application.ProcessMessages;
end;
function Splash_Create: TForm;
// создание формы заставки
var
tmpImage: TImage;
tmpLabel: TLabel;
tmpImageFileName: string;
begin
Result := TForm.Create(Application);
with Result do
begin
Name := SPLASH_FORM_NAME;
Width := SPLASH_LOGO_WIDTH + SPLASH_BORDER * 2;
Height := SPLASH_LOGO_HEIGHT + SPLASH_BORDER * 2;
BorderStyle := bsNone;
Position := poScreenCenter;
Color := SPLASH_BORDER_COLOR;
StyleElements := 0;
end;
// добавить картинку
tmpImage := TImage.Create(Result);
with tmpImage do
begin
Parent := Result;
Width := SPLASH_LOGO_WIDTH;
Height := SPLASH_LOGO_HEIGHT;
Top := SPLASH_BORDER;
Left := SPLASH_BORDER;
tmpImageFileName := ExtractFilePath(Application.ExeName) + SPLASH_FILE_NAME;
if not FileExists(tmpImageFileName) then
RaiseException('Splash_Create() Не найден файл ' + tmpImageFileName)
else
Picture.LoadFromFile(tmpImageFileName);
end;
// добавить название
tmpLabel := TLabel.Create(Result);
with tmpLabel do
begin
Name := 'AppName';
Parent := Result;
Top := 0;
Left := 0;
Autosize := False;
Width := Result.ClientWidth;
Height := Result.ClientHeight;
Alignment := taCenter;
Layout := tlCenter;
WordWrap := True;
StyleElements := 0;
// название приложения берём из константы APP_NAME
Caption := APP_NAME; // {VClass\AppConstVar.pas}
Font.Size := SPLASH_FONT_SIZE;
Font.Color := SPLASH_FONT_COLOR
end;
// добавить тень
Label_AddShadow(tmpLabel, SPLASH_SHADOW_COLOR); // {VClass\Label.pas}
// добавить номер версии
tmpLabel := TLabel.Create(Result);
with tmpLabel do
begin
Name := 'AppVer';
Parent := Result;
// номер версии связан с системой лицензирования; кроме номера может содержать признак демо-версии
Caption := License_GetVersion; // {VClass\License.pas}
StyleElements := 0;
Font.Size := SPLASH_VER_FONT_SIZE;
Font.Color := SPLASH_VER_FONT_COLOR;
Left := Height div 2;
Top := Result.ClientHeight - Height - (Height div 2);
end;
// добавить тень
Label_AddShadow(tmpLabel, SPLASH_SHADOW_COLOR, 1); // {VClass\Label.pas}
// добавить строку прогресса
tmpLabel := TLabel.Create(Result);
with tmpLabel do
begin
Name := 'labProgress';
Parent := Result;
Caption := '';
StyleElements := 0;
Font.Size := SPLASH_VER_FONT_SIZE;
Font.Color := SPLASH_VER_FONT_COLOR;
Align := alBottom;
Alignment := taRightJustify;
Layout := tlTop;
Autosize := False;
Height := Height + Height div 2;
end;
end;
begin
end.
Code language: Delphi (delphi)
Использование
Хронология запуска приложения, созданного в My Visual Database выглядит так:
# | Процесс | Время |
---|---|---|
1 | Загрузка приложения (*.exe) в память | Неизменно. Зависит от производительности компьютера |
2 | Загрузка графических ресурсов (graphics.dll) в память | Зависит от количества статических изображений в проекте – размерафайла graphics.dll |
3 | Создание всех форм, созданных в конструкторе форм (forms.xml) | Зависит от количества и сложности форм проекта – размера файла forms.xml. |
4 | Инициализация скриптов | Выполнение кода, написанного в секции begin end. модуля script.pas и всех подключенных модулей |
5 | Подключение к базе данных | Зависит от количества компонент TdbComboBox. |
6 | Отображение формы аутентификации и авторизации (опционально) | Форма отображается, если задействована штатная система настройки прав доступа. |
7 | Отображение главной формы |
Таким образом, заставку можно отобразить либо на этапе 4, либо на этапе 7. В нижеприведенном примере заставка отображается на этапе отображения главной формы, в обработчике события OnShow.
В процедуре frmMain_OnShow много условностей, связанных с работой приложения, но вам нужно обратить внимание на последовательность вызовов функции Splash_Create() и метода Splash_ShowProgress().
procedure frmMain_OnShow(Sender: TObject; Action: string);
// отображение главной формы
var
tmpForm: TForm;
tmpStartTime: TTime;
tmpSleepTime: integer;
begin
//
tmpForm := Splash_Create;
tmpForm.Show;
Application.ProcessMessages;
tmpStartTime := Time();
//
// тут должна быть всяческая инициализация, которая может занять продолжительное время
App_InitSystemVar; // инициализация системных переменных
Resource_Init();
Resource_CreateMenu(frmMain);
Images_Init; // инициализация подcистемы загрузки картинок
Style_Init; // активация стиля
Style_AddToMenu(frmMain);
UserApp_InitForm; // создать формы
UserApp_InitVar; // настроить переменные
CodeHL_Init; // настройка подсветки кода
// инициализация, которая идет после настройки переменных
CE_Init(MainForm); // главное меню находится на главной форме, но отображается там, где нужно
Hotkey_Init(MainForm); // подключить горячие клавиши
UserApp_InitBase; // инициализация базы данных
//
// гарантированная задержка, но не более указанной в APP_SPLASH_DELAY
tmpSleepTime := SPLASH_DELAY_MIN - Trunc((Time() - tmpStartTime) * 24 * 60 * 60 * 1000);
if tmpSleepTime > 0 then
Sleep(tmpSleepTime);
tmpForm.Close;
tmpForm.Free;
//
// отобразить в заголовке главной формы название и версию программы
frmMain.Caption := R('APP_NAME', APP_NAME) + ' ' + License_GetVersion;
frmMain.pgcMain.ActivePage := frmMain.tshNavigation; // открыть вкладку "Навигация"
frmMain.pgcNavigation.ActivePage := frmMain.tshTask; // открыть вкладку "Задачи"
end;
procedure UserApp_InitForm;
// инициализация форм
var
tmpItem: TMenuItem;
begin
try
Splash_ShowProgress('Инициализация форм...' );
// App_SetDoubleBuffer;
UserApp_AddFrame;
// добавляем сплиттеры и выравнивание
Splitter_Create(frmMain.panUnit, frmMain.pgcUnitProc, alTop); //
Splitter_Create(frmMain.panProject, frmMain.pgcProject, alLeft); //
Splitter_Create(frmMain.panClass, frmMain.pgcClass, alLeft); // классы
Splitter_Create(frmMain.panType, frmMain.pgcType, alLeft); // типы
Splitter_Create(frmMain.panFunction, frmMain.pgcFunction, alLeft); // функции
Splitter_Create(frmMain.pgcMethodParamView, frmMain.panMethod, alBottom); // методы
Splash_ShowProgress('Создание форм...' );
DTF_CreateForms; // загрузить формы из файла настроек форм
Splash_ShowProgress('Подготовка...' );
frmMain.panProjectToolbar.Align := alBottom;
// статические формы
Form_ShowOnWinControl(frmSearchResult, frmMain.tshSearchResult);
Form_ShowOnWinControl(frmExampleView, frmMain.tshExample);
//
UserApp_InitAboutForm;
except
RaiseException('UserApp_InitForm() ' + ExceptionMessage);
end;
end;
procedure DTF_CreateForms;
// создание динамических форм по списку, находящемуся в файле dforms.ini
var
tmpIniFile: TIniFile;
tmpFormList: TStringList;
i: integer;
tmpForm: TForm;
tmpSName: string;
function S(AName: string): string;
begin
Result := tmpIniFile.ReadString(tmpFormList.Strings(i), AName, '');
end;
begin
try
tmpIniFile := TIniFile.Create(ExtractFilePath(Application.ExeName) + DTF_FORMS_INI_FILE);
tmpFormList := TStringList.Create;
tmpIniFile.ReadSections(tmpFormList);
for i := 0 to tmpFormList.count - 1 do
begin
Splash_ShowProgress('Создание формы для таблицы '+ S('table') );
tmpSName := 'DTF_' + UpperCase(tmpFormList.Strings(i)) + '_CAPTIONS'; // формат ресурсов
tmpForm := DTF_Create(tmpFormList.Strings(i), S('table'), S('fields'), R(tmpSName, S('captions')), S('sort'),
S('parentField'), S('masterField'), S('filter'), S('isDetail') = 'True');
Form_ShowOnWinControl(tmpForm, TWinControl(FindCF(S('parentControl'))));
end;
tmpFormList.Free;
tmpIniFile.Free;
except
RaiseException('DTF_CreateForms() - ' + ExceptionMessage);
end;
end;
Code language: Delphi (delphi)
В строке 9 создается форма заставки, а затем делается видимой (строка 10). Далее выполняются различные действия, и там, где задержки реально большие, используются вызовы процедуры Splash_ShowProgress(): строки 48, 58, 60 и 93. После того, как инициализация завершена, форма заставки уничтожается (строки 33-34).
Время отображения
Время отображения заставки задается константой SPLASH_DELAY_MIN. Это – минимальное гарантированное время отображения в миллисекундах. Разумеется, если фактическое время инициализации будет больше заявленного, заставка будет отображаться до завершения инициализации.
В строке 12 запоминается время начала отображения заставки, а после инициализации сверяется с текущим и при необходимости делается дополнительная задержка (строки 30-32), чтобы обеспечить заданное время отображения заставки.
Спасибо больше, доступно объяснили и расставили все по полочкам.
большое*
А я благодарю вас за обратную связь, которая позволяет мне писать более понятные статьи 🙂