Procedure TTestForm.RunButtonClick(Sender: TObject); begin

LoadFile;

QuestionNumber:=0;

FalseNumber:=0;

NextButton.Enabled:=true;

NextQuestion; end;

В первой строке я вызываю процедуру LoadFile, которую я напишу чуть позже, и она будет загружать список вопросов из выбранного файла проекта. Почему я должен загружать вопросы каждый раз при старте программы? Да потому что тест будет происходить следующим образом:

1. 1. Из списка вопросов случайным образом выбирается первый попавшийся вопрос.

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

3. 3. При следующем старте теста список вопросов инициализируется заново (мы снова загружаем весь список) и все вопросы возвращаются на свои места.

 

После загрузки вопросов я обнуляю все переменные, и делаю доступной кнопку NextButton (это кнопка «Дальше», по нажатию которой будет выбираться следующий вопрос). При старте программы кнопка «Дальше» должна быть недоступной.

В последней строке я вызываю процедуру NextQuestion, которая и будет выбирать случайный вопрос и отображать его в окне программы. Теперь посмотрим на процедуру загрузки вопросов LoadFile. Она идентична уже написанной процедуре загрузки в программе редактора вопросов:

procedure TTestForm.LoadFile;

var
fs:TFileStream;
i, Count:Integer;
Str:String[5];
ProjectName:String[255];
NewQuest:PQuestion;

begin
QuestionList.Clear;
//Открыть файл для чтения
fs:=TFileStream.Create(FileName, fmOpenRead);

//Перейти в начало файла и прочитать заголовок
fs.Seek(0,soFromBeginning);
fs.read(Str, SizeOf(Str));

//Если заголовок равен тексу "Тест", значит это "вопрос-
//варианты ответов".
if Str='Тест' then

begin
//Прочитать имя проекта
fs.Read(ProjectName, sizeof(ProjectName));
Caption:=ProjectName;

try
//Прочитать количество вопросов
fs.Read(Count, sizeof(Count));


 

//Запустить цикл чтения вопросов
for i:=0 to Count-1 do
begin

//Создаю новую структуру в памяти для вопроса NewQuest:=New(PQuestion); //Читаю структуру fs.Read(NewQuest^, sizeof(TQuestion));


 

//Добавляю структуру в контейнер
QuestionList.Add(NewQuest);
end;

finally
//Закрываю файл
fs.Free;

end; end; end;

Тут всё должно быть понятно, но я на всякий случай снабдил весь код подробными комментариями.

Теперь посмотрим на процедуру NextQuestion, которая должна случайным образом выбирать вопрос из списка:

procedure TTestForm.NextQuestion; var i:Integer;

begin Randomize; Question:=Random(QuestionList.Count-1);

QuestionLabel.Caption:=PQuestion(QuestionList[Question]).Name;

QuestionCheckList.Items.Clear; for i:=0 to PQuestion(QuestionList[Question]).ResultCount-1 do QuestionCheckList.Items.Add(PQuestion(QuestionList[Question]).ResiltText[i]);

Inc(QuestionNumber); end;

В первой строке процедуры я вызываю процедуру Randomize, которая инициализирует таблицу случайных чисел. Если ты опустишь вызов этой процедуры, то ничего страшного не произойдёт, и когда ты будешь запрашивать случайное число, то оно будет случайным, но всё же лучше инициализировать таблицу. Просто в этом случае считается, что случайность будет более случайной (во как звучит!!!).

Во второй строке я вызываю функцию Random, которая возвращает случайное число. Ей нужно передать в качестве параметра максимально допустимое число. Я передаю QuestionList.Count–1, т.е. количество вопросов в нашем списке. Функция вернёт мне случайное число от 0 до указанного числа. Я сохраняю это число в переменной Question.

В следующей строке кода я показываю в компоненте QuestionLabel вопрос соответствующий вопрос. Затем очищаю список ответов в компоненте QuestionCheckList и заполняю его вариантами ответов, относящихся к данному вопросу. В последней строке кода я увеличиваю переменную QuestionNumber, в которой у нас храниться количество отвеченных вопросов.

По нажатию кнопки «Далее» пишем следующий код:

procedure TTestForm.NextButtonClick(Sender: TObject);

var OK:Boolean; i:Integer;

begin OK:=true;

for i:=0 to PQuestion(QuestionList[Question]).ResultCount-1 do if PQuestion(QuestionList[Question]).ResiltValue[i]<>QuestionCheckList.Checked[i] then OK:=false;

if OK=false then Inc(FalseNumber);

//Удаление вопроса из списка QuestionList.Delete(Question);

if QuestionNumber<5 then NextQuestion else begin Application.MessageBox(PChar('Вы закончили тест с количеством ошибок = '+ IntToStr(FalseNumber)), 'Внимание!!!'); NextButton.Enabled:=false; end; end;

В первой строке здесь устанавливается логическая переменная ОК в значение true. В этой переменной мы будем хранить состояние результата ответа. По умолчанию будем считать, что ответ правильный, поэтому и устанавливаем значение true.

Далее, запускаю цикл от 0 до количества вариантов ответов в списке. Внутри цикла я сравниваю значение правильных ответов с состоянием свойство Checked компонента QuestionCheckList. Если хоть что-то не совпадает, то тестируемый где-то ошибся и нужно установить переменную ОК в значение false, т.е. ответ неверный. После цикла происходит проверка, если переменная ОК равна false, то увеличиваем счётчик неправильных ответов FalseNumber на единицу.

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

Дальше происходит проверка, если количество отображённых вопросов меньше 5, то выбираем следующий вопрос (вызываем процедуру NextQuestion), иначе отображаем сообщение с состоянием пройденного теста и делаем кнопку «Далее» недоступной.

Как видишь, мой тест состоит из 5 вопросов, если тебе нужно больше, то можешь увеличить это значение. Но у нас в редакторе вопросов есть кнопка свойств, по нажатию которой можно отображать окно свойств проекта. Я бы сделал возможность в этом окне выбирать количество вопросов, на которые должен ответить испытуемый. Потом эти свойства можно сохранить в файл проекта и загружать в нашей программе теста. Но всё это я делать не буду, потому что у тебя уже есть достаточно знаний, чтобы попробовать всё это сделать самому.

На компакт диске, в директорииПримерыГлава 25Test4ты можешь увидеть исходник уже написанного примера.