C++ Builder - вопросы программирования



             

Листинг 10 2



Листинг 10.2.

Программа тестирования /* Универсальная .программа тестирования. Тест загружается из файла, имя которого должно быть указано в командной строке. Программа демонстрирует создание и настройку компонентов во время работы программы. */ # include <vcl.h> #pragma hdrstop #include "tester_.h" #include <stdio.h // для доступа к функции sscanf #pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1;  // форма int f; // дискриптор файла теста // имя файла теста берем из командной строки int level[4];// кол-во правильных ответов, необходимое // для достижения уровня AnsiString mes[4]; // сообщение о достижении уровня TVopros Vopros; // вопрос int otv; // номер выбранного ответа int right =0; // кол-во правильных ответов // функции, обеспечивающие чтение вопроса из файла теста int Getlnt(int f); // читает целое int GetString(int f, AnsiString *st); // читает строку

// конструктор __fastcall TForml::TForml(TComponent* Owner) : TFormfOwner) { int i; int left =10; // создадим радиокнопки для выбора // правильного ответа, но сделаем их невидимыми for (i =0; i < 4; i++) { // создадим радиокнопку RadioButton[i] = new TRadioButton(Forml); // установим значения свойств RadioButton[i]->Parent = Forml; RadioButton[i]->Left = left; RadioButton[i]->Width = Forml->ClientWidth - left - 20; RadioButton[i]->Visible = false; RadioButton[i]->Checked = false; // зададим функцию обработки события Click RadioButton[i]->OnClick = RadioButtonClick; } } void __fastcall TForml::FormActivate(TObject *Sender) { AnsiString st; // имя файла теста должно быть указано в командной строке int n = ParamCount(); if (  n < 1) { Labell->Font->Style = TFontStyles()« fsBold; Labell->Caption = "В командной строке запуска надо задать имя файла теста"; Buttonl->Tag = 2; return; } // открыть файл теста f = FileOpen(ParamStr(1), fmOpenRead); if ( f == -1) { Labell->Font->Style = TFontStyles()« fsBold; Labell->Caption = "Ошибка доступа к файлу теста " + ParamStr(l); Buttonl-XTag = 2; return; } // вывести информацию о тесте GetString(f, Sst); // прочитать название теста Forml->Caption = st; GetString(f, sst); // прочитать вводную информацию Labell->Width = Forml->ClientWidth - Labell->Left -20; Labell->Caption = st; Labell->AutoSize = true; // прочитать информацию об уровнях оценки for (int i=0; i<4; i++) { level[i] = Getlnt(f); GetString(f, &mes[i]); } } // читает из файла очередной вопрос bool GetVopros(TVopros *v) { AnsiString st; int p;  // если р=1, то к вопросу есть иллюстрация if ( GetStringtf, &(v->Vopr)) != 0) { // прочитать кол-во вариантов ответа,  номер правильного ответа // и признак наличия иллюстрации v->nOtv = Getlnt(f); v->rOtv = Getlnt(f); p = Getlnt(f); if   (p)   // к вопросу есть иллюстрация GetString(f,&(v->Img)); else v->Img = ""; // читаем варианты ответа for(int i = 0;i < v->nOtv; i++) { GetString(f,&(v->0tv[i])); } return true; } else return false; } // выводит вопрос void __fastcall TForml::ShowVopros(TVopros v) { int top; int i; // вопрос Labell->Width = ClientWidth - Labell->Left -20; Labell->Caption = v.Vopr; Labell->AutoSize = true; if   (v.Img != //к вопросу есть иллюстрация { /* определим высоту области, которую можно использовать для вывода иллюстрации */ int RegHeight = Buttonl->Top - (Labell-ХГор + Labell->Height +10) // область вывода вопроса - (RadioButton[l]->Height + 10) * v.nOtv; Image1->Top = Labell->Top + Labell->Height + 10; // загрузим картинку и определим ее размер Image1->Visible = false; Image1->AutoSize = true; Image1->Picture->LoadFromFile(v.Img); if (Imagel->Height > RegHeight) // картинка не помещается ( Image1->AutoSize = false; Imagel->Height = RegHeight; Imagel->Proportional = true; ) Image1->Visible = true; // положение полей отсчитываем от иллюстрации top = Imagel-ХГор + Imagel->Height + 10; ) else // положение полей отсчитываем от вопроса top = Labell-ХГор + Labell->Height + 10; // варианты ответа for (i =0; i < v.nOtv; i++) { RadioButton[i]->Top = top; RadioButton[i]->Caption = v.0tv[i]; RadioButton[i]->Visible = true; RadioButton[i]->Checked = false; top += 20; } } // щелчок на радиокнопке выбора ответа void__fastcall TForml::RadioButtonClick(TObject *Sender) { int 1=0; while (  ! RadioButton[i]->Checked) 1++; otv = 1+1; // ответ выбран, сделаем доступной кнопку Дальше Buttonl->Enabled = true; } // удаляет вопрос с экрана
void __fastcall TForml::EraseVopros(void) { Imagel->Visible = false; // скрыть поле вывода иллюстрации // скрыть поля выбора ответа for (int i=0; i <4; i++) ( RadioButton[i]->Visible = false; RadioButton[i]->Checked = false; } Button1->Enabled = false; // сделать недоступной кнопку Дальше } // щелчок на кнопке ОК/Дальше/ОК void __fastcall TForml::ButtonlClick(TObject *Sender) { bool ok; // результат чтения из файла очередного вопроса switch (Buttonl-XTag) { case 0: // щелчок на кнопке ОК в начале работы программы // прочитать и вывести первый вопрос GetVopros(SVopros); ShowVopros(Vopros); Buttonl->Caption = "Дальше"; Buttonl->Enabled = false; Button1->Tag = 1; break; case 1: // щелчок на кнопке Дальше if (otv == Vopros.rOtv) // выбран правильный ответ right++; EraseVopros(}; ok = GetVopros(SVopros); if ( ok) ShowVopros(Vopros) ; else // вопросов больше нет { FileClose(f); // вывести результат AnsiString st; // сообщение int i; // номер достигнутого уровня Form1->Caption = "Результат тестирования"; st.printf("Правильных ответов: %i\n",right); // определим оценку i = 0; // предположим, что испытуемый // ответил на все опросы while (( right < level[i]) && (i < 3)) i++; st = st + mes[i]; Labell->Caption = st; Button1->Caption = "OK"; Buttonl->Enabled = true; Buttonl->Tag =2; } break; case 2: // щелчок на кнопке OK в конце работы программы Form1->Close(}; // завершить работу программы } } // Функция GetString читает строку из файла // значение функции — количество прочитанных символов int GetString(int f, AnsiString *st) { unsigned char buff300]; // строка (буфер) unsigned char *p = buf;  // указатель на строку int n; // кол-во прочитанных байт   (значение функции FileRead) int len =0;   // длина строки n = FileRead(f, p, 1); while ( n != 0) { if ( *p == '\r') { n = FileRead(f, p, 1); // прочитать '\n' break; } len++; P++; n = FileRead(f, p, 1); } *p = '\0'; if ( len !=0) st->printf("%s", buf); return len; } // читает из файла целое число int Getlnt(int f) { char buf[20];  // строка (буфер) char *p = buf;  // указатель на строку int n;// кол-во прочитанных байт (значение функции FileRead) int a; // число, прочитанное из файла n = FileRead(f, p, 1); while ( (*p >= '0') (*p <= '9') && (n > 0)) { P++; n = FileRead(f, p, 1) ; } if ( *p == '\r') n = FileRead(f,   p,   1) // прочитать   '\n' *p =  '\0'; // изображение числа в буфере, преобразуем строку в целое sscanf(buf,"%i", &a); return a; }
Как было сказано ранее, объявление массива компонентов не создает компоненты, а только устанавливает факт их существования. Создает и настраивает компоненты RadioButton конструктор формы (функция TForm1: : TForm1) . Непосредственное создание компонента (элемента массива) выполняет оператор RadioButton[i] = new TRadioButton(Forml)

Следующие за этим оператором инструкции обеспечивают настройку компонента. В том числе, они путем присваивания значения свойству Onclick задают функцию обработки события click . В рассматриваемой программе для обработки события click на всех компонентах RadioButton используется одна и та же функция, которая путем опроса значения свойства checked фиксирует номер выбранного ответа и делает доступной кнопку Дальше (Button1) .

После запуска программы и вывода на экран стартовой формы происходит событие onActivate . Функция обработки этого события проверяет, указан ли в командной строке параметр — имя файла теста. Реализация программы предполагает, что если имя файла теста задано без указания пути доступа к нему, т файл теста и файлы с иллюстрациями находятся в том же каталоге, что и программа тестирования. Если же путь доступа указан, то файлы с иллюстрациями должны находиться в том же каталоге, что и файл теста. Такой подход позволяет сгруппировать все файлы одного теста в одном каталоге.

Если файл теста задан, функция открывает его, читает название теста и вводную информацию и затем выводит их в диалоговое окно, причем название выводится в заголовок, а вводная информация — в поле Label1 .

Непосредственное чтение строк из файла выполняет функция Getstring . Значением функции является длина строки. Следует обратить внимание на то, что функция GetString Возвращает строку Ans iString.

После того как прочитана общая информация о тесте, программа считывает из файла теста информацию об уровнях оценки и фиксирует ее в массивах level и mes . Критерий достижения уровня (количество правильных ответов) считывает функция Getint .

После вывода информационного сообщения программа ждет, пока пользователь не нажмет кнопку Ok (Button№ ).

Командная кнопка Button№ используется:

  •  для завершения работы программы, если в командной строке не указан файл теста;
  •  для активизации процесса тестирования (после вывода информационного сообщения);
  •  для перехода к следующему вопросу (после выбора варианта ответа);
  •  для завершения работы программы (после вывода результата тестирования).


Таким образом, реакция программы на нажатие кнопки Buttonl зависит от состояния программы. Состояние программы фиксирует свойство Tag кнопки Button 1 .

После вывода информации о тесте значение свойства Tag кнопки Button1 равно нулю. Поэтому в результате щелчка на кнопке Button 1 выполняется та часть программы, которая обеспечивает вывод первого вопроса и замену находящегося на кнопке текста ОК на текст Дальше, и заменяет значение свойства Tag на единицу.

В процессе тестирования значение свойства Tag кнопки Button 1 равно единице. Поэтому функция обработки события click сравнивает номер выбранного ответа (увеличенный на единицу номер компонента RadioButton ) с номером правильного ответа, увеличивает на единицу счетчик правильных ответов (в том случае, если выбран правильный ответ) и активизирует процесс чтения следующего вопроса. Если попытка чтения очередного вопроса завершилась неудачно (это значит, что вопросы исчерпаны), функция выводит результаты тестирования, заменяет текст на командной кнопке на ОК и подготавливает операцию завершения работы программы (свойству Tag присваивает значение 2).

Чтение вопроса (вопрос, информация о количестве альтернативных ответов, номер правильного ответа и признак наличия иллюстрации, а также имя файла иллюстрации и альтернативные ответы) из файла теста выполняет функция GetVopros .

Вывод вопроса, иллюстрации и альтернативных ответов выполняет функция showVopros . Сначала функция выводит вопрос — присваивает значение свойству caption компонента Labe l1 . Затем, если к вопросу есть иллюстрация, функция вычисляет размер области, которую можно выделить для отображения иллюстрации, и загружает иллюстрацию. Если размер иллюстрации больше размера области, функция устанавливает максимально возможный размер компонента imagei и присваивает значение false свойству AutoSize и true — свойству Proportional , обеспечивая тем самым масштабирование иллюстрации. После этого функция выводит альтернативные ответы. Положение компонентов, обеспечивающих вывод альтернативных ответов, отсчитывается от нижней границы компонента image 1 , если к вопросу есть иллюстрация, или компонента Label1 , если иллюстрации нет.

Сразу после вывода вопроса кнопка Дальше (Button1) недоступна. Сделано это для того, чтобы блокировать возможность перехода к следующему вопросу, если не дан ответ на текущий. Доступной кнопку Дальше делает функция обработки события Click на одном из компонентов RadioButton .

Кроме того, функция путем сканирования (проверки значения свойства checked компонентов массива RadioButton ) определяет, на каком из компонентов массива испытуемый сделал щелчок и, следовательно, какой из вариантов ответа выбран. Окончательная фиксация номера выбранного ответа и сравнение его с номером правильного ответа происходит в результате нажатия кнопки Дальше.

 











Содержание  Назад  Вперед