At the request of a reader of my blog, this article examines in detail the algorithm for displaying the application launch progress.
In the article “Border Splash Screen” I talked about how to create a splash form that should appear before the main form and remain on the screen for some time. And at this very time, the program performs actions related to preparation for work: initializes variables and arrays, loads graphic resources – in a word, prepares the application for work. This is especially in demand for complex applications in which forms are created or modified using scripts. This technique is also used in projects that use the MySQL DBMS, since it becomes possible to initialize components before the main form is launched.
The splash form is created by scripts. It contains a background image (1), the project name with a shadow (2), the version number (3), and a launch progress bar (4).
Splash.pas
The “Developer’s Handbook” project uses a modular structure – scripts required to perform a certain class of tasks are stored in separate files – modules. The splash.pas module contains the function for creating a splash form Splash_Create() and the procedure for displaying a form with information about the loading progress Splash_ShowProgress(), as well as constants that determine the operation of the splash form.
The Splash.pas module references other modules: AppConstVar.pas, Label.pas and License.pas, which are located in the Script\VClass folder. That is, to use this module, your project must also contain modules associated with it. In addition to explicit references, there are implicit dependencies on other modules, such as System\Utils.pas. All source files of the project can be found in the program description.
Below is the module’s table of contents with detailed comments and links to related modules.
// Splash
// 05.05.2023
// function Splash_Create:TForm; - create a splash form
// procedure Splash_ShowProgress( AText: string ); - display loading progress
uses 'VClass\AppConstVar.pas', 'VClass\Label.pas', 'VClass\License.pas';
const
SPLASH_FORM_NAME = 'frmSplash'; // form name
SPLASH_DELAY_MIN = 2000; // minimum splash display time;
SPLASH_LOGO_WIDTH = 350; // Image width
SPLASH_LOGO_HEIGHT = 169; // Image height
SPLASH_BORDER = 2; // image border thickness
SPLASH_BORDER_COLOR = clBlack; // frame color
SPLASH_FILE_NAME = 'Images\Logo\logo_350x169.jpg'; // background image file
SPLASH_FONT_SIZE = 28; // font size for title
SPLASH_FONT_COLOR = clWhite; // color for title
SPLASH_SHADOW_COLOR = clBlack; // shadow color
SPLASH_VER_FONT_SIZE = 11; // font size for version
SPLASH_VER_FONT_COLOR = clWhite; // font color for version
procedure Splash_ShowProgress( AText: string );
// show loading progress
var
tmpLabel: TLabel;
tmpForm: TForm;
begin
// find the splash form by name; this search allows you to do without a global variable
tmpForm := GetFormByName(SPLASH_FORM_NAME); // {System\Utils.pas}
// find the label component on the form; the search is also performed by the component name
FindC(tmpForm,'labProgress',tmpLabel); // {System\Utils.pas}
// write the progress text into the label caption; the space at the end is added for aesthetics,
// since the label content is aligned to the right.
tmpLabel.Caption := AText + ' ';
// call the method that allows you to process all system messages;
// this is necessary for drawing the new content of the label caption
Application.ProcessMessages;
end;
function Splash_Create: TForm;
// create a splash form
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;
// add an image 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() File not found ' + tmpImageFileName) else Picture.LoadFromFile(tmpImageFileName);
end;
// add a title 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;
// the application name is taken from the constant APP_NAME
Caption := APP_NAME; // {VClass\AppConstVar.pas}
Font.Size := SPLASH_FONT_SIZE;
Font.Color := SPLASH_FONT_COLOR
end;
// add shadow
Label_AddShadow(tmpLabel, SPLASH_SHADOW_COLOR); // {VClass\Label.pas}
// add version number
tmpLabel := TLabel.Create(Result);
with tmpLabel do
begin
Name := 'AppVer';
Parent := Result;
// the version number is associated with the licensing system; in addition to the number, it may contain a demo version flag
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;
// add a shadow Label_AddShadow(tmpLabel, SPLASH_SHADOW_COLOR, 1); // {VClass\Label.pas} // add a progress line 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)
Usage
The chronology of running an application created in My Visual Database looks like this:
# | Process | Time |
---|---|---|
1 | Loading an application (*.exe) into memory | Unchanged. Depends on computer performance |
2 | Loading graphic resources (graphics.dll) into memory | Depends on the number of static images in the project – the size of the graphics.dll file |
3 | Create all forms created in the form designer (forms.xml) | Depends on the number and complexity of the project forms – the size of the forms.xml file. |
4 | Initialization of scripts | Execution of code written in the begin end section of the script.pas module and all connected modules |
5 | Connecting to a database | Depends on the number of TdbComboBox components. |
6 | Display authentication and authorization form (optional) | The form is displayed if the standard access rights setting system is enabled. |
7 | Displaying the main form |
Thus, the splash screen can be displayed either in stage 4 or in stage 7. In the example below, the splash screen is displayed in the main form’s display stage, in the OnShow event handler.
The frmMain_OnShow procedure contains many conventions related to the operation of the application, but you need to pay attention to the sequence of calls to the Splash_Create() function and the Splash_ShowProgress() method.
procedure frmMain_OnShow(Sender: TObject; Action: string);
// display the main form
var
tmpForm: TForm;
tmpStartTime: TTime;
tmpSleepTime: integer;
begin
//
tmpForm := Splash_Create;
tmpForm.Show;
Application.ProcessMessages;
tmpStartTime := Time();
//
// all sorts of initialization should be here, which can take a long time
App_InitSystemVar; // initialization of system variables
Resource_Init();
Resource_CreateMenu(frmMain);
Images_Init; // initialization of the image loading subsystem
Style_Init; // style activation
Style_AddToMenu(frmMain);
UserApp_InitForm; // create forms
UserApp_InitVar; // set up variables
CodeHL_Init; // set up code highlighting
// initialization that occurs after setting up variables
CE_Init(MainForm); // the main menu is on the main form, but is displayed where needed
Hotkey_Init(MainForm); // enable hotkeys
UserApp_InitBase; // initialize the database
//
// guaranteed delay, but no more than specified in APP_SPLASH_DELAY
tmpSleepTime := SPLASH_DELAY_MIN - Trunc((Time() - tmpStartTime) * 24 * 60 * 60 * 1000);
if tmpSleepTime > 0 then
Sleep(tmpSleepTime);
tmpForm.Close;
tmpForm.Free;
//
// display the program name and version in the main form header
frmMain.Caption := R('APP_NAME', APP_NAME) + ' ' + License_GetVersion;
frmMain.pgcMain.ActivePage := frmMain.tshNavigation; // open the "Navigation" tab
frmMain.pgcNavigation.ActivePage := frmMain.tshTask; // open the "Tasks" tab
end;
procedure UserApp_InitForm;
// initializing forms
var
tmpItem: TMenuItem;
begin
try
Splash_ShowProgress('Initializing forms...' );
// App_SetDoubleBuffer;
UserApp_AddFrame;
// adding splitters and alignment
Splitter_Create(frmMain.panUnit, frmMain.pgcUnitProc, alTop); //
Splitter_Create(frmMain.panProject, frmMain.pgcProject, alLeft); //
Splitter_Create(frmMain.panClass, frmMain.pgcClass, alLeft); // classes
Splitter_Create(frmMain.panType, frmMain.pgcType, alLeft); // types
Splitter_Create(frmMain.panFunction, frmMain.pgcFunction, alLeft); // functions
Splitter_Create(frmMain.pgcMethodParamView, frmMain.panMethod, alBottom); // methods
Splash_ShowProgress('Creating forms...' );
DTF_CreateForms; // load forms from form settings file
Splash_ShowProgress('Preparing...' );
frmMain.panProjectToolbar.Align := alBottom;
// static forms
Form_ShowOnWinControl(frmSearchResult, frmMain.tshSearchResult);
Form_ShowOnWinControl(frmExampleView, frmMain.tshExample);
//
UserApp_InitAboutForm;
except
RaiseException('UserApp_InitForm() ' + ExceptionMessage);
end;
end;
procedure DTF_CreateForms;
// creating dynamic forms from the list located in the dforms.ini file
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('Creating a form for the table '+ S('table') );
tmpSName := 'DTF_' + UpperCase(tmpFormList.Strings(i)) + '_CAPTIONS'; // resource format 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)
Line 9 creates the splash form and then makes it visible (line 10). Various actions are performed next, and where the delays are really long, calls to the Splash_ShowProgress() procedure are used: lines 48, 58, 60, and 93. After the initialization is complete, the splash form is destroyed (lines 33-34).
Display time
The splash screen display time is set by the constant SPLASH_DELAY_MIN. This is the minimum guaranteed display time in milliseconds. Of course, if the actual initialization time is longer than the declared one, the splash screen will be displayed until the initialization is complete.
Line 12 remembers the splash screen display start time, and after initialization, it is compared with the current one and, if necessary, an additional delay is made (lines 30-32) to ensure the specified splash screen display time.