Очевидно, що наведений вище текст програми був би набагато прошу і витонченішою, якби поля виведення альтернативних відповідей і перемикачі вибору відповідей були б об’єднані в масиви. тоді програма могла б звертатися до полів і перемикачів не по імені, а за індексом.
Delphi дозволяє об’єднати компоненти в масив, проте створюватися такі компоненти повинні не під час створення форми додатка, а динамічно – під час роботи програми.
На рис. 15.7 наведено вид форми вдосконаленого додатки.
Рис. 15.7. Форма додатки Тест, версія 2
На формі відсутні поля виведення альтернативних відповідей і перемикачі вибору правильної відповіді. Вони будуть створені під час роботи програми.
Оголошення масиву компонентів нічим не відрізняється від оголошення звичайного масиву – вказується ім’я масиву, діапазон зміни індексу і тип елементів масиву. нижче наведено оголошення масивів компонентів форми, що розробляється:
answer: array [1..N_ANSWERS] of TLabel; // альтернативні відповіді selector: array [1..N_ANSWERS + 1] of TRadioButton; // кнопки вибору відповіді
Однак, для того щоб компонент з’явився у формі, одного оголошення недостатньо. Компонент – це об’єкт Delphi, і його оголошення – це тільки покажчик на область пам’яті, який без наявності об’єкта ні на що не вказує. Створюється компонент застосуванням методу Create до покажчика на компонент, в нашому випадку – до елементу масиву.
Наприклад, інструкції
answer [1] : = TLabel.Create (self); answer [1] .Parent : = Form1;
створюють компонент Label і поміщають його в форму.
Після створення компонента програма повинна виконати його настройку, т. е. ту роботу, яку під час створення форми додатка виконує програміст за допомогою Object Inspector. Під налаштуванням розуміється присвоювання початкових значень тим властивостям компонента, зумовлені значення яких не відповідають вимогам, що пред’являються.
Якщо компонент повинен реагувати на деякий подія, то. потрібно написати процедуру обробки цього події і помістити оголошення створеної процедури в оголошення типу форми. Наприклад, оголошення типу форми, що розробляється повинно виглядати так:
type TForm1 = Class (TForm) Label5: TLabel; // поле виведення питання Image1: TImage; // область виведення ілюстрації Panel1: TPanel; Button1: TButton; // кнопка Ok, Далі, Завершити procedure FormActivate (Sender: TObject); procedure FormCreate (Sender: TObject); procedure ButtonlClick (Sender: TObject); procedure SelectorClick (Sender: TObject); private { Private declarations} public { Public declarations} end;
На відміну від інших, згенерованих Delphi, рядків оголошення типу, рядок procedure SelectorClick (Sender: TObject) вставлена. У оголошення вручну.
Примітка
При створенні процедури обробки події для звичайного компонента (компонента, який доданий в форму під час розробки форми програми) Delphi автоматично генерує заготівлю процедури обробки події і її оголошення. Програміст повинен написати тільки інструкції процедури.
У разі створення процедури обробки події для компонента, який створюється динамічно, програміст повинен повністю написати текст процедури і помістити її оголошення в оголошення форми.
Після того як буде написана процедура обробки події, потрібно зв’язати цю процедуру з конкретним компонентом. Робиться це шляхом присвоєння імені процедури обробки властивості, ім’я якого збігається з ім’ям оброблюваного події. Наприклад, інструкціяselector [1] .OnClick : = SelectorClick;
задає процедуру обробки події Onclick для компонента selector [i]. У лістингу 15.2 наведено повний текст програми Тест, версія 2.
Лістинг 15.2. Програма тестування, версія 2 unit test2 _; interface uses SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls; type TForm1 = class (TForm) Label5: TLabel; // поле виведення питання Image1: TImage; // область виведення ілюстрації Panel1: ТPanel; Button1: TButton; // кнопка Ok, Далі, Завершити procedure FormActivate (Sender: TObject); procedure FormCreate (Sender: TObject); procedure ButtonlClick (Sender: TObject); procedure SelectorClick (Sender: TObject); private { Private declarations} public { Public declarations} end; var Form1: TForm1; // форма implementation const N_ANSWERS = 4; // чотири варіанти відповідей N_LEVEL = 4; // чотири рівні оцінки var // динамічно створювані компоненти answer: array [1..N_ANSWERS] of TLabel; // альтернативні відповіді selector: array [1..N_ANSWERS + 1] of TRadioButton; // кнопки вибору відповіді f: TextFile; fn: string; // ім'я файлу питань level: array [1..N_LEVEL] of integer; // сума, що відповідає рівню mes: array [1..N_LEVEL] of string; // повідомлення, що відповідає рівню score: array [1..N_ANSWERS] of integer; // окуляри за вибір відповіді summa: integer; // набрано очок vopros: integer; // номер поточного питання n_otv: integer; // число варіантів відповіді otv: integer; // номер обраного відповіді // установка форми в початковий стан Procedure ResetForm (frm: TForm1); var i: integer; begin for i: = 1 to N_ANSWERS do begin answer [i] .width: = frm.ClientWidth-answer [i] .left- 5; answer [i] .Visible: = FALSE; Selector [i] .Visible: = FALSE; end; frm. Label5.width: = frm.ClientWidth-frm.Label5.left-5; frm. Image1.Visible: = False; end; // визначення досягнутого рівня procedure Itog (suirana: integer; frm: TForm1); var i: integer; buf: string; begin buf: = > ;; str (summa: 5, buf); buf: = 'Результати тестування' + chr (13) + 'Всього балів: '+ buf; i: = 1; while (summa < level [i]) and (i < N_LEVEL) do i: = i + l; buf: = buf + chr (13) + mes [i]; frm.Labels.caption: = buf; end; procedure TForm1.FormCreate (Sender: TObject); var i: integer; begin // створимо п'ять міток для виведення питання і альтернативних відповідей for i: = l to N_ANSWERS do begin answer [i]: = TLabel.Create (self); answer [i] .Parent: = Forml; answer [i] .Left: = 36; answer [i] .Wordwrap: = True; end; // створимо перемикачі для вибору відповіді for i: = l to N_ANSWERS + 1 do begin selector [i]: = TRadioButton.Create (self); selector [i] .Parent: = self; selector [i] .Caption: = ''; selector [i] .Width: = 17; selector [i] .Left: = 16; selector [i] .Visible: = False; selector [i] .Enabled: = True; selector [i] .OnClick: = SelectorClick; end; ResetForm (Forml); end; // висновок початкової інформації про тест procedure info (var f: TextFile; l: TLabel); var s, buf: string; begin buf: = ''; repeat readln (f, s); if s [l] <>'.' then buf: = buf + s + ''; until s [l] = '.'; Form1.Labels.caption: = buf; end; // прочитати інформацію про оцінки за тест Procedure GetLevel (var f: TextFile); var i: integer; buf: string; begin // заповнюємо значення глобальних масивів i: = 1; repeat readln (f, buf); if buf [1] <>'.' then begin mes [i]: = buf; readln (f, level [i]); i: = i + 1; end; until buf [1] = '.'; end; // масштабування ілюстрації Procedure ScalePicture; var w, h: integer; // максимально допустимі розміри картинки scaleX: real; // коеф. масштабування по X scaleY: real; // коеф. масштабування по Y scale: real; // загальний коеф. масштабування i: integer; begin // обчислити максимально допустимі розміри картинки w: = Form1.ClientWidth-Form1.Labels.Left; h: = Form1.ClientHeight - Form1.Panel1.Height -5 - Form1.Label5.Top - Forml.Label5.Height - 5; for i: = 1 to N_ANSWERS do if answer [i] .Caption <> '' then h: = h-answer [i] .Height-5; // тут визначена максимально допустима величина ілюстрації // визначити масштаб if w > Form1.Image1.Picture.Width then scaleX: = 1 else scaleX: = w / Forml.Image1.Picture.Width; if h > Forml.Image1.Picture.Height then scaleY: = 1 else scaleY: = h / Form1.Image1.Picture.Height; if ScaleYOcaleX then scale: = scaleY else scale: = scaleX; // тут масштаб визначений Form1.Image1.Top: = Form1.Label5.Top + Forml.LabelS.Height + 5; Form1.Image1.Left: = Form1.Label5.Left; Form1.Image1.Width: = Round (Form1.Image1.Picture.Width * scale); Form1.Image1.Height: = Round (Form1.Image1.Picture.Height * scale) Form1.Label5.Visible: = TRUE; end; // висновок питання на екран Procedure VoprosToScr (var f: TextFile; frm: TForm1; var vopros: integer), var i: integer; code: integer; s, buf : string; ifn: string; // файл ілюстрації begin vopros: = vopros + 1 ; str (vopros: 3, s); frm. caption: = 'Питання' + s; // виведемо текст питання buf: = ""; repeat readln (f, s); if (s [l] <> '.') and (s [l] <> '\') then buf: = buf + s + ''; until (s [l] = '.'} or (s [l] = '\'); frm.Labels.caption: = buf; if s [l] = '\' then // до питання є ілюстрація begin frm.Image1.Tag: = 1; ifn: = copy (s, 2, length (s)); try frm.Image1.Picture.LoadFromFile (ifn); except on E: EFOpenError do frm.tag: = 0; end // try end else frm. Image1.Tag: = 0; // читаємо варіанти, відповідей for i: = 1 to N_ANSWERS do begin answer [i] .caption: = ''; answer [i] .Width: = frm.ClientWidth-Form1.Label5.Left-5 ; end; i: = l; repeat buf : = "" ; repeat // читаємо текст варіанти відповіді readln (f, s); if (S [l] <>; '.') and (s [1] <> ',') then buf: = buf + s + ''; until (s [1] = ',') or (s [l] = '.'); // прочитаний альтернативний відповідь val (S [2], score [i], code); answer [i] .caption: = buf; i: = i + l; until s [1] = '. '; // тут прочитана ілюстрація і альтернативні відповіді if Form1.Image1.Tag = 1 // є ілюстрація до питання? then begin ScalePicture; Forml.Image1.Visible: = TRUE; end; // висновок альтернативних відповідей i: = 1; while (answer [i] .caption <>) and (i < = N_ANSWERS) do begin if i = 1 then if frm.Image1.Tag = 1 then answer [1] .top: = frm.Image1.Top + frm.Image1.Height + 5 else answer [i] .top: = frm.Label5.Top + frm.Label5.Height + 5 else answer [I]. top: = answer [i-1]. top + answer [i-1]. height + 5; selector [i] . top: = answer [i]. top; selectorfi], visible: = TRUE; answer [I]. visible: = TRUE; i: = i + l; end; end; {$ R * .DFM} procedure TForml. FormActivate (Sender: TOb j ect); begin ResetForm (Forml); if ParamCount = 0 then begin Label3 . font. color: = clRed; Label5. caption: = 'He заданий файл питань тесту. 1 ; Buttonl . caption: = 'Ok'; Buttonl.tag: = 2; Buttonl . Enabled: = TRUE end else begin fn: = ParamStr (1); assignf ile (f, fn); {$! -} reset (f); if IOResult = 0 then begin Inf <> (F, Label3); GetLevel (f) ; end; summa: = 0; end; end; procedure TForm1. ButtonlClick (Sender: TObject) begin case Button1.tag of 0: begin Button1.caption: = 'Далі'; Buttonl.tag: = 1; Selector [N_ANSWERS + 1] .Checked: = TRUE; // вивід першого питання Buttonl.Enabled: = False; ResetForm (Forml); VoprosToScr (f, Forml, vopros) end; 1: begin // висновок інших питань summa: = summa + score [otv]; Selector [N_ANSWERS + 1] .Checked: = TRUE; Button1.Enabled: = False; ResetForm (Form1); if not eof (f) then VoprosToScr (f, Forml, vopros) else begin closefile (f); Button1.caption: = 'Ok'; Forml.сарtiоn: = 'Результат'; Buttonl.tag: = 2; Buttonl.Enabled: = TRUE; Itog (summa, Form1); end; end; 2: begin // завершення роботи Form1.Close; end; end; end; // клацання на кнопці вибору відповіді procedure TForml.SelectorClick (Sender: TObject); var i: integer; begin while selector [i] .Checked = FALSE do i: = i + l; otv: = i; Buttonl.enabled: = TRUE; end; end.
У порівнянні з першим варіантом програма Тест, версія 2 володіє істотною перевагою. Для її модернізації, наприклад для збільшення кількості альтернативних відповідей, досить змінити тільки опис іменованої константи N_ANSWERS.