Skip to Content

Усовершенствование программы

Усовершенствование программы

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

Delphi позволяет объединить компоненты в массив, однако создаваться такие компоненты должны не во время создания формы приложения, а динамически — во время работы программы.

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


Рис. 15.7. Форма приложения Тест, версия 2

На форме отсутствуют поля вывода альтернативных ответов и переключатели выбора правильного ответа. Они будут созданы во время работы программы.

Объявление массива компонентов ничем не отличается от объявления обычного массива — указывается имя массива, диапазон изменения индекса и тип элементов массива. Ниже приведено объявление массивов компонентов формы разрабатываемой программы:

  1. answer: array[1..N_ANSWERS] of TLabel;
  2.  
  3. // альтернативные ответы selector:
  4.  
  5. array[1..N_ANSWERS+1] of TRadioButton;
  6.  
  7. // кнопки выбора ответа

Однако, для того чтобы компонент появился в форме, одного объявления недостаточно. Компонент — это объект Delphi, и его объявление — это только указатель на область памяти, который без наличия объекта ни на что не указывает. Создается компонент применением метода Create к указателю на компонент, в нашем случае — к элементу массива.

Например, инструкции

  1. answer[1] := TLabel.Create(self) ;
  2. 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

  1. unit test2_;
  2. interface
  3.  
  4. uses
  5. SysUtils, WinTypes, WinProcs,
  6. Messages, Classes, Graphics,
  7. Controls, Forms, Dialogs,
  8. StdCtrls, ExtCtrls;
  9.  
  10. type
  11.  
  12. TForm1 = class(TForm)
  13.  
  14. Label5: TLabel; // поле вывода вопроса
  15. Image1: TImage; // область вывода иллюстрации
  16. Panel1: ТPanel; Button1: TButton;
  17.  
  18. // кнопка Ok, Дальше, Завершить
  19.  
  20. procedure FormActivate(Sender: TObject);
  21. procedure FormCreate(Sender: TObject);
  22. procedure ButtonlClick(Sender: TObject);
  23. procedure SelectorClick(Sender: TObject);
  24.  
  25. private
  26. { Private declarations }
  27. public
  28. { Public declarations }
  29. end;
  30.  
  31. var
  32. Form1: TForm1; // форма
  33.  
  34. implementation
  35. const
  36.  
  37. N_ANSWERS=4; // четыре варианта ответов
  38. N_LEVEL=4; // четыре уровня оценки
  39.  
  40. var
  41.  
  42. // динамически создаваемые компоненты
  43.  
  44. answer: array[1..N_ANSWERS] of TLabel;
  45.  
  46. // альтернативные ответы
  47.  
  48. selector: array[1..N_ANSWERS+1] of TRadioButton;
  49.  
  50. // кнопки выбора ответа
  51.  
  52. f:TextFile;
  53. fn:string; // имя файла вопросов
  54. level:array[1..N_LEVEL] of integer;
  55.  
  56. // сумма, соответствующая уровню
  57.  
  58. mes:array[1..N_LEVEL] of string;
  59.  
  60. // сообщение, соответствующее уровню
  61.  
  62. score:array[1..N_ANSWERS] of integer;
  63.  
  64. // очки за выбор ответа
  65.  
  66. summa:integer; // набрано очков
  67. vopros:integer; // номер текущего вопроса
  68. n_otv:integer; // число вариантов ответа
  69. otv:integer; // номер выбранного ответа
  70.  
  71. // установка формы в исходное состояние
  72.  
  73. Procedure ResetForm(frm:TForm1);
  74. var
  75.  
  76. i:integer;
  77.  
  78. begin
  79.  
  80. for i:=1 to N_ANSWERS do
  81. begin
  82.  
  83. answer[i].width:=frm.ClientWidth-answer[i].left-5;
  84. answer[i].Visible:=FALSE; Selector[i].Visible:=FALSE;
  85.  
  86. end;
  87.  
  88. frm. Label5.width:=frm.ClientWidth-frm.Label5.left-5;
  89. frm. Image1.Visible:=False;
  90.  
  91. end;
  92.  
  93. // определение достигнутого уровня
  94.  
  95. procedure Itog(suirana:integer;frm:TForm1);
  96. var
  97.  
  98. i:integer; buf:string;
  99.  
  100. begin
  101.  
  102. buf: = ";
  103. str(summa:5,buf); buf:='Результаты тестирования'+chr(13)
  104. +'Всего баллов: '+buf; i:=1;
  105.  
  106. while (summa < level[i]) and (i<N_LEVEL) do
  107. i:=i+l;
  108. buf:=buf+chr(13)+mes[i];
  109. frm.Labels.caption:=buf;
  110.  
  111. end;
  112.  
  113. procedure TForm1.FormCreate(Sender: TObject);
  114. var
  115.  
  116. i: integer;
  117.  
  118. begin
  119.  
  120. // создадим пять меток для вывода вопроса и альтернативных ответов
  121.  
  122. for i:=l to N_ANSWERS do
  123. begin
  124.  
  125. answer[i]:=TLabel.Create(self);
  126. answer[i].Parent:=Forml;
  127. answer[i].Left:=36;
  128. answer[i].Wordwrap:=True;
  129.  
  130. end;
  131.  
  132. // создадим переключатели для выбора ответа
  133.  
  134. for i:=l to N_ANSWERS+1 do
  135. begin
  136.  
  137. selector[i]:=TRadioButton.Create(self);
  138. selector[i].Parent:=self;
  139. selector[i].Caption:='';
  140. selector[i].Width:=17;
  141. selector[i].Left:=16;
  142. selector[i].Visible:=False;
  143. selector[i].Enabled:=True;
  144. selector[i].OnClick:=SelectorClick;
  145.  
  146. end;
  147.  
  148. ResetForm(Forml);
  149.  
  150. end;
  151.  
  152. // вывод начальной информации о тесте
  153.  
  154. procedure info(var f:TextFile;l:TLabel);
  155. var
  156.  
  157. s,buf:string;
  158.  
  159. begin
  160.  
  161. buf:='';
  162.  
  163. repeat
  164.  
  165. readln(f,s);
  166.  
  167. if s[l]<>'.' then
  168. buf:=buf+s+' ';
  169.  
  170. until s[l] ='.';
  171.  
  172. Form1.Labels.caption:=buf;
  173.  
  174. end;
  175.  
  176. // прочитать информацию об оценках за тест
  177.  
  178. Procedure GetLevel(var f:TextFile);
  179. var
  180.  
  181. i:integer;
  182. buf:string;
  183.  
  184. begin // заполняем значения глобальных массивов i:=1;
  185.  
  186. repeat
  187.  
  188. readln(f,buf);
  189.  
  190. if buf[1] <> '.' then
  191. begin
  192.  
  193. mes[i]:=buf;
  194. readln(f,level[i]);
  195. i:=i+1;
  196.  
  197. end;
  198.  
  199. until buf[1]='.';
  200.  
  201. end;
  202.  
  203. // масштабирование иллюстрации
  204.  
  205. Procedure ScalePicture;
  206. var
  207.  
  208. w,h:integer; // максимально допустимые размеры картинки
  209. scaleX:real; // коэф. масштабирования по X
  210. scaleY:real; // коэф. масштабирования по Y
  211. scale:real; // общий коэф. масштабирования
  212. i:integer;
  213.  
  214. begin
  215.  
  216. // вычислить максимально допустимые размеры картинки
  217.  
  218. w:=Form1.ClientWidth-Form1.Labels.Left;
  219. h:=Form1.ClientHeight
  220. - Form1.Panel1.Height -5
  221. - Form1.Label5.Top
  222. - Forml.Label5.Height - 5;
  223.  
  224. for i:=1 to N_ANSWERS do
  225.  
  226. if answer[i].Caption <> '' then
  227. h:=h-answer[i].Height-5;
  228.  
  229. // здесь определена максимально допустимая величина иллюстрации
  230. // определить масштаб
  231.  
  232. if w>Form1.Image1.Picture.Width then
  233. scaleX:=1
  234. else
  235. scaleX:=w/Forml.Image1.Picture.Width;
  236.  
  237. if h>Forml.Image1.Picture.Height then
  238. scaleY:=1
  239. else
  240. scaleY:=h/Form1.Image1.Picture.Height;
  241.  
  242. if ScaleYOcaleX then
  243. scale:=scaleY
  244. else
  245. scale:=scaleX; // здесь масштаб определен
  246.  
  247. Form1.Image1.Top:=
  248. Form1.Label5.Top+Forml.LabelS.Height+5;
  249. Form1.Image1.Left:=Form1.Label5.Left;
  250. Form1.Image1.Width:=
  251. Round(Form1.Image1.Picture.Width*scale);
  252. Form1.Image1.Height:=
  253. Round(Form1.Image1.Picture.Height*scale)
  254. Form1.Label5.Visible:=TRUE;
  255.  
  256. end;
  257.  
  258. // вывод вопроса на экран
  259.  
  260. Procedure VoprosToScr(var f:TextFile;
  261. frm:TForm1;var vopros:integer),
  262. var
  263.  
  264. i:integer;
  265. code:integer;
  266. s,buf:string;
  267. ifn:string; // файл иллюстрации
  268.  
  269. begin
  270.  
  271. vopros:=vopros+1 ;
  272. str(vopros:3,s);
  273. frm. caption: ='Вопрос' + s;
  274.  
  275. // выведем текст вопроса
  276.  
  277. buf: = ";
  278.  
  279. repeat
  280.  
  281. readln(f, s) ;
  282. if (s[l] <> '.') and (s[l] <> '\') then
  283. buf:=buf+s+' ';
  284.  
  285. until (s[l] ='.'} or (s[l] = '\');
  286.  
  287. frm.Labels.caption:=buf;
  288.  
  289. if s[l] = '\' then // к вопросу есть иллюстрация
  290. begin
  291.  
  292. frm.Image1.Tag:=1; ifn:=copy(s,2,length(s));
  293.  
  294. try
  295. frm.Image1.Picture.LoadFromFile(ifn); except
  296. on E:EFOpenError do
  297. frm.tag:=0; end //
  298. try
  299. end
  300. else
  301. frm. Image1.Tag: =0;
  302.  
  303. // читаем варианты, ответов
  304.  
  305. for i:=1 to N_ANSWERS do
  306. begin
  307.  
  308. answer[i].caption:='';
  309. answer[i].Width:=frm.ClientWidth-Form1.Label5.Left-5;
  310.  
  311. end;
  312.  
  313. i:=l;
  314.  
  315. repeat
  316.  
  317. buf: = " ;
  318.  
  319. repeat // читаем текст варианта ответа
  320.  
  321. readln(f,s);
  322.  
  323. if (s[l]<>'.') and (s[1] <> ',') then
  324. buf:=buf+s+' ';
  325.  
  326. until (s[1]=',')or(s[l]='.');
  327.  
  328. // прочитан альтернативный ответ
  329.  
  330. val (s[2],score[i],code);
  331. answer[i].caption:=buf;
  332.  
  333. i:=i+l;
  334.  
  335. until s [1] = '.'; // здесь прочитана иллюстрация и альтернативные ответы
  336.  
  337. if Form1.Image1.Tag =1 then// есть иллюстрация к вопросу?
  338. begin
  339.  
  340. ScalePicture;
  341. Forml.Image1.Visible:=TRUE;
  342.  
  343. end;
  344.  
  345. // вывод альтернативных ответов
  346.  
  347. i:=1;
  348.  
  349. while (answer[i].caption <> ") and (i <= N_ANSWERS) do
  350. begin
  351.  
  352. if i = 1 then
  353.  
  354. if frm.Image1.Tag =1 then
  355. answer[1].top:=frm.Image1.Top+frm.Image1.Height+5
  356. else
  357. answer[i].top:=frm.Label5.Top+frm.Label5.Height+5
  358. else
  359. answer [i] . top:=answer [i-1] .top+ answer [i-1] . height+5;
  360. selector[i] . top:=answer [i] .top;
  361. selectorfi] ,visible:=TRUE;
  362. answer [i] . visible : =TRUE; i:=i+l;
  363.  
  364. end;
  365. end;
  366.  
  367. {$R *.DFM}
  368.  
  369. procedure TForml . FormActivate ( Sender : TOb j ect ) ;
  370. begin
  371.  
  372. ResetForm ( Forml ) ;
  373.  
  374. if ParamCount = 0 then
  375. begin
  376.  
  377. Label3 . font . color : =clRed;
  378. Label5. caption: = 'He задан файл вопросов теста.1';
  379. Buttonl . caption : = ' Ok ' ; Buttonl.tag:=2;
  380. Buttonl . Enabled : =TRUE
  381. end
  382. else
  383. begin
  384.  
  385. fn:=ParamStr (1) ;
  386. assignf ile ( f , fn) ;
  387.  
  388. {$!-}
  389. reset (f) ;
  390.  
  391. if IOResult=0 then
  392. begin
  393.  
  394. Inf <> (f, Label3) ;
  395. GetLevel(f) ;
  396.  
  397. end;
  398.  
  399. summa:=0;
  400.  
  401. end;
  402. end;
  403.  
  404. procedure TForm1. ButtonlClick (Sender: TObject)
  405. begin
  406.  
  407. case Button1.tag of
  408. 0:
  409. begin
  410.  
  411. Button1.caption:='Дальше';
  412. Buttonl.tag:=1;
  413. Selector[N_ANSWERS+1].Checked:=TRUE; // вывод первого вопроса
  414. Buttonl.Enabled:=False;
  415. ResetForm(Forml);
  416. VoprosToScr(f,Forml,vopros)
  417.  
  418. end;
  419.  
  420. 1:
  421. begin // вывод остальных вопросов
  422.  
  423. summa:=summa+score[otv];
  424. Selector[N_ANSWERS+1].Checked:=TRUE;
  425. Button1.Enabled:=False; ResetForm(Form1);
  426. if not eof(f) then
  427. VoprosToScr(f,Forml,vopros)
  428. else
  429. begin
  430.  
  431. closefile(f);
  432. Button1.caption:='Ok';
  433. Forml.сарtiоn:='Результат';
  434. Buttonl.tag:=2; Buttonl.Enabled:=TRUE;
  435. Itog(summa,Form1);
  436.  
  437. end;
  438. end;
  439.  
  440. 2:
  441. begin // завершение работы
  442.  
  443. Form1.Close;
  444.  
  445. end;
  446. end;
  447. end;
  448.  
  449. // щелчок на кнопке выбора ответа
  450.  
  451. procedure TForml.SelectorClick(Sender: TObject);
  452. var
  453.  
  454. i: integer;
  455.  
  456. begin
  457.  
  458. while selector[i].Checked = FALSE do
  459.  
  460. i:=i+l;
  461. otv:=i;
  462. Buttonl.enabled:=TRUE;
  463.  
  464. end;
  465.  
  466. end.

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



Delphi Для профессионалов &1   |    Delphi Для профессионалов &2   |    Delphi Для профессионалов &3   |    Иллюстрированный самоучитель &1   |    Иллюстрированный самоучитель &2