Привёдённый выше текст программы Delphi тесты был бы проще и изящней, если поля вывода альтернативных ответов и переключатели выбора альтернативных ответов были объединены в массивы. Тогда программа могла бы обращаться к полям и переключателям не по имени, а по индексу.
Delphi позволяет объединить компоненты в массив, но создаваться такие компоненты должны не во время создания формы приложения, динамически – во время работы программы.

Ниже приведён вид формы усовершенствованного приложения тесты на Delphi.

Усовершенствование программы на Delphi ТЕСТЫ


Как видете на форме отсутствуют поля вывода альтернативных ответов и переключателей выбора правильного ответа.
Объявление массива компонентов ничем не отличается  от объявления обычного массива, то есть указывается имя массива, диапазон изменения индекса и тип элементов массива. Ниже показано объявление массивов компонентов формы усовершенствованного приложения тесты на Delphi :
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 и помещают его на форму.
После создания компонента программа должна выполнить его настройку, то есть ту работу, которую во время создания формы приложения выполняет программист  при помощи объект инспектор. Под настройкой понимается присвоения начальных значений тем свойствам компонента, значения которых не отвечают предъявляемым требованиям.
И если компонент должен реагировать на некоторое событие , то нужно написать процедуру обработки этого события и поместить объявление созданной нами процедуры в объявление типа формы. Ниже приведён пример, объявления типа формы разрабатываемого усовершенствованного приложения тесты на Delphi:

В отличие от других, сгенерированных Delphi, строк объявления типа, строка procedure SelectorClick(Sender: TObject) вставлена в объявление вручную.
При создании процедуры обработки события для обычного компонента (компонента, который добавлен в орму во время разработки формы программы) Delphi автоматически генерирует заготовку процедуры обработки события и её объявление. Программист должен написать только инструкции процедуры.
В случае создания процедуры обработки события для компонента, который создаётся динамически, программист должен полностью написать текст процедуры и поместить её объявление в объявление формы.
После того как  будет написана процедура обработки события, нужно связать эту процедуру с конкретным компонентом. Для этого  нужно присвоить имя процедуры обработки свойству имя которого совпадает с именем обрабатываемого события. Например, инструкция
Selector[1].OnClick:= SelectorClick;
Задаёт процедуру обработки события OnClick для компонента Selector[1].
Нижеприведён листинг усовершенствованного приложения тесты на Delphi.
unit UTest3;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComCtrls, ExtCtrls, ToolWin;

type
TForm1 = class(TForm)
Label5: TLabel;
Image1: TImage;
Panel1: TToolBar;
Button1: TButton;

  
procedure FormActivate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure SelectorClick(Sender: TObject);
procedure FormCreate(Sender: TObject);

private
{ Private declarations }
public
{ Public declarations }

end;
var
Form1: TForm1;

implementation

const
N_ANSWERS=4; // 4 варианта ответов
N_LEVEL=4; //  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(summa: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
i:=i+1;
buf:=buf+Chr(13)+mes[i];
frm.Label5.Caption:=buf;
end;

 

 

procedure Info(var f:TextFile;l:TLabel);
var
s,buf:string;
begin
buf:='';
repeat
readln(f,s);
if s[1] <> '.'
then buf := buf+s+'';
until s[1] = '.';
Form1.Label5.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;//коэф. масштабирования по Х
scaleY:real;// коэф. масштабирования поУ
scale:real;// общий коэф. масштабирования
i:integer;
begin
//вычислить мак. доп-е размеры картинки
w:=Form1.ClientWidth-Form1.Label5.Left;
h:=Form1.ClientHeight
-Form1.Panel1.Height-5
-Form1.Label5.Top
-Form1.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/Form1.Image1.Picture.Width;
if h>Form1.Image1.Picture.Height
then scaleY:=1
else scaleY:=h/Form1.Image1.Picture.Height;
if ScaleY
then scale:=scaleY
else scale:=scaleX;
//здесь масштаб определен
Form1.Image1.Top:=Form1.Label5.Top+Form1.Label5.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 VoprosToStr(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[1] <> '.') or (s[1] <> '\')
then buf:=buf+s+'';
until (s[1] ='.') or (s[1] = '\');
frm.Label5.Caption:=buf;

      if s[1] = '\'
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:=1;
repeat
buf:='';
repeat //читаем  текст варианта ответа
readln(f,s);
if (s[1]<>'.') and (s[1] <> ',')
then buf:=buf+s+'';
until (s[1]=',')or(s[1]= '.');
//прочитан альтернативный ответ
val(s[2],score[i],code);
answer[i].Caption:=buf;
i:=i+1;
until s[1]='.';
//здесь прочитана иллюстрация и альтернативные ответы

                  if Form1.Image1.Tag=1 // есть иллюстрация к вопросу?
then begin
ScalePicture;
Form1.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;
selector[i].Visible:=True;
answer[i].Visible:=True;
i:=i+1;
end;
end;

{$R *.dfm}
procedure TForm1.FormActivate(Sender: TObject);
begin
ResetForm(Form1);
if ParamCount = 0
then begin
Label5.Caption:= 'Не задан файл вопросов теста';
Button1.Caption:='Ok';
Button1.Tag:=2;
Button1.Enabled:=True
end
else begin
fn :=ParamStr(1);

    assignfile(f,fn);
{$I-}
reset(f);
{I+}
if IOResult=0 then
begin
Info(f,Label5);// прочитать и вывести информацию о тесте
GetLevel(f);// прочитать информацию об уровнях оценок
end;
summa:=0;
end;

end;

 

 

 

procedure TForm1.Button1Click(Sender: TObject);
begin
begin
case Button1.Tag of
0: begin
Button1.Caption:='Дальше';
Button1.Tag:=1;
Selector[N_ANSWERS+1].Checked:=True;
// вывод 1 вопроса
Button1.Enabled:=False;
ResetForm(Form1);
VoprosToStr(f,Form1,vopros)
end;
1: begin //
summa:=summa+score[otv];
Selector[N_ANSWERS+1].Checked:=True;
Button1.Enabled:=False;
ResetForm(Form1);
if not eof(f)
then VoprosToStr(f,Form1,vopros)
else
begin

closefile(f);
Button1.Caption:='Ok';
Form1.Caption:='';
Button1.Tag:=2;
Button1.Enabled:=True;
Itog(summa,Form1);
end;
end;
2: begin // Завершение работы
Form1.Close;
end;

  end;

end;
end;
procedure TForm1.SelectorClick(Sender: TObject);
var
i:integer;
begin
i:=1;
while selector[i].Checked=False do
i:=i+1;
otv:=i;
Button1.Enabled:=True;
end;
procedure TForm1.FormCreate(Sender: TObject);
var
i:integer;
begin
//создадим 5 меток для вывода вопроса и альтернативных ответов
for i:=1 to N_ANSWERS do
begin
answer[i]:=TLabel.Create(self);
answer[i].Parent:=Form1;
answer[i].Left:=36;
answer[i].WordWrap:=True;
end;

     //создадим переключатели для выбора ответа
for i:=1 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(Form1);
end;

 

end.

По сравнению с первым вариантом программы тест, версия 2 обладает существенным преимуществом. Для её модернизации, например для увеличения количества альтернативных ответов, достаточно изменитьописание именованной константы N_ANSWERS.

 

Скачать исходник программы на Delphi «Тесты» »:

 

Добавить комментарий


Защитный код
Обновить