Программа психологических тестов
Автор благоларит jurii jurii@karelia.ru, за идею этого урока.
На этом уроке мы закрепим знания, полученные в прошлых уроках, и как всегда, изучим много нового.
Программа "Тесты" скорее всего, может пригодиться многим. Тем более что мы ее будем писать, изначально универсальной. Вопросы с ответами будет подгружаться из специального текстового файла.
Рассмотрим пошаговую работу над созданием программы.
Каждую глобальную задачу, как и программу, необходимо разбивать на отдельные, мелкие задачи, определить пути и методы решения. Не определив изначально пути решения, можно постепенно войти в тупик. В тупике задача становится невыполнимой, приходится все начинать сначала. Поэтому вы всегда должны четко представлять свою задачу и четко представлять общие направления решения, возможно даже алгоритм решения проблемы.
Итак, вот пример такого планирования.
Сам текст теста располагается в текстовом файле. Значит, в одном текстовом файле у нас будет храниться вся необходимая для него информация, в том числе и служебная, относительно скрытая от пользователя. Почему мы делаем именно текстовый файл, а не работаем с двоичным, потому что заносить данные в такой файл легко, не требуется писать отдельной программы создания файлов теста, написать и изменить можно в любом текстовом редакторе.
Для планировки структуры такого файла мы должны разбить всю хранимую в нем информацию на отдельные строки. Эти строки могут быть объединены по одинаковым признакам. Читаться такой файл программой будет по строкам.
Каждый тест должен имееть следующую структуру:
1. Название.
2. Количество вопросов.
<Начало блока вопросов>
3. Вопрос.
4. Возможные варианты ответов (для нашей программы сделаем четыре варианта ответов).
5. Баллы, присуждаемые за тот или иной ответ.
<Конец блока вопросов>
6. Несколько вариантов результатов теста, зависимых от набранных баллов, которые показываются тестируемому человеку (для нашей программы сделаем четыре результата теста).
Все жестко заданные количества строк легко изменить, сделав соответствующие изменения в коде программы, или добавив дополнительный параметр в файл теста. Возможно, если с вашей стороны будет интерес, эту тему мы продолжим рассматривать в следующих уроках.
Тест, взятый для примера в этом уроке, был любезно выдерт мною из первого попавшегося журнала на столе моей сестры. Это журнал "Cool girl" №23 от 1998 года. Староват, да ничего, подойдет. Да простят меня мужчины, но это тест для молодых девушек. Впрочем, если что не нравится - текстовый редактор вам в руки и вперед. Если вы отошлете файл своих собственных тестов (рабочий, проверенный файл тестов, в частности, ищу тест определения темперамента человека) мне, автору уроков, то это будет замечательно. Все ваши тесты постараюсь разместить прямо в этом уроке.
Как создать файл тестов. Создаем в любом текстовом редакторе, желательно в блокноте windows новый файл.
Важно, в таком редакторе убрать опцию "автоперенос слов", чтобы нас не сбивало с толку перенесенные строки, ведь они у нас иногда будут очень длинными.
Первая строчка в файле должна содержать название теста. Для моего теста "Умеешь ли ты отдыхать?". Каждая такая строчка должна завершаться нажатием на клавишу Enter (перевод строки).
Следующая строка содержит число вопросов в тесте. Их у нас 16.
Дальше у нас следует блок вопросов и ответов на них. Этот блок должен повторяться количество раз, заданное в строке количества вопросов - 16.
Первая строка - вопрос.
Вторая строка - первый ответ.
Третья строка - количество баллов за этот ответ.
Четвертая строка - второй ответ.
и т.д.
В конце файла теста следуют результаты теста.
Первая строка - число, начальный интервал баллов.
Вторая строка - число, конечный интервал баллов.
Третья строка - вариант результата теста.
и т.д. для всех результатов теста.
Файл теста мы с вами рассмотрели, теперь примемся за написание программы.
Снова спланируем нашу задачу.
1. Сделаем, чтобы при запуске программы, она спрашивала у нас, какой тест необходимо загрузить. Если теста нет или пользователь отказывается открывать тест, то программа завершает свою работу.
2. При успешном выборе теста, программа открывает этот файл, считывает название, считывает количество вопросов, и предлагает первый вопрос с ответами на него.
3. После перебора всех вопросов открывается окно результатов теста.
4. Завершение работы программы.
Внешний вид программы можно спланировать так, вопрос и возможные варианты ответов будут размещены на разных панелях. На панелях разместим компоненты отображения текста (Label) и по одной кнопке на каждый вариант ответа.
Окно результатов теста планировать не будем, текст результата можно высветить на первых порах разработки программы с помощью команды ShowMessage. В последствии вы можете самостоятельно дорабатывать уже свою программу красочным оформлением, интересными особенностями.
В delphi создаем новый проект.
Для главной формы проекта Form1 устанавливаем свойство начальной позиции окна Position по центру рабочего стола poDesktopCenter. Свойтсво BorderStyle в bsDialog, чтобы окно имело неизменяемый размер.
Оформляем внешний вид программы.
В форме размещаем пять компонентов Panel, после каждого добавленного компонента изменяем свойство Align в alTop, и убирая текст из свойства Caption. Для последнего, пятого компонента Panel5 свойство Align устанавливаем в alClient.
Для более красочного оформления и отличия панели вопроса от панелей ответов, панель Panel1 свойство BevelInner установим в bvLowered. Установим размер компонентов и формы сверху вниз, как показано на рисунке.
Теперь в панелях разместим по компоненту Label, убрав свойство автоматического изменения размера AutoSize в False, и установив свойство автоматического перевода строки WordWrap в True. Если текст не будет помещаться по длине компонента Label, то эти слова будут перенесены в другую строку. Для каждого вопроса устанавливаем по одной кнопке Button (или BitBtn для размещения в кнопке рисунка). Переименовываем названия кнопок на названия "Ответ 1", "Ответ 2" и т.п.
После размещения всех визуальных компонентов в программу, установим размер компонентов Label1 - Label5 в максимально возможное, чтобы вопросы и ответы полностью были видны.
Для улучшения восприятия вопросов и ответов для этих же компонентов установите свойство шрифта Font на свой вкус.
Последний компонент - OpenDialog для открытия файла вопросов. Можете установить фильтр для открываемых файлов, например только для файлов с расширением TES для того, чтобы случайно не открыть другой, не файл теста.
Окончательный внешний вид окна вашей программы смотрите на этом рисунке.
Теперь примемся за написание кода программы.
Объявим несколько глобальных переменных, которые будут действительны за пределами одного модуля.
После слова implementation
в редакторе кода пишем:
Var f: TextFile; // текстовый файл теста
FOpen: Boolean; // признак открытого файла
TestName: String; // название теста
QCurrent: Integer; // текущий номер вопроса
QCount: Integer; // всего вопросов в тесте
QUser: Integer; // сумма баллов
В инспекторе объектов выбираем компонент Form1 и для события (страница Events) OnShow (сработает до отображения окна на экране) пишем:
procedure TForm1.FormShow(Sender: TObject);
begin
FOpen:=false; // снимаем признак открытого файла (мы его еще не открывали)
if OpenDialog1.Execute then // если файл теста выбран,
begin // то:
AssignFile(f,OpenDialog1.FileName); // привязка имени выбранного файла в диалоге к файловой переменной
{$I-} // отключения автоматической обработки ошибок ввода-вывода
Reset(f); // открываем файл
{$I+} // возвращаем опцию контроля ошибок ввода-вывода
if IOResult<>0 then // если была ошибка открытия файла, то
begin
MessageDLG('Ошибка открытия файла',mtError,[mbOk],0); // сообщение на экран
Application.Terminate; // завершить работу программы
end;
FOpen:=true; // включаем признак, что у нас файл был открыт
ReadLn(f,TestName); // читаем из файла название теста
Form1.Caption:='Психологический тест - '+TestName; // заголовок окна устанавливаем в название теста
ReadLn(f,QCount); // читаем количество вопросов теста
QCurrent:=0; // текущий вопрос - нулевой
QUser:=0; // сброс количества баллов за ответы
LoadQuestion; // переход к процедуре чтения вопроса
end else Application.Terminate; // если файл теста не выбран, то завершить работу программы
end;
Единственной непонятной строкой для компилятора будет строка LoadQuestion. Эту процедуру мы создадим самостоятельно. В редакторе кода в разделе public объявляем новую процедуру:
procedure LoadQuestion;
А после написанной нами процедуры реакции на появления окна, после строки окончания процедуры "end;", но до строки окончания модуля "end." создадим самостоятельно заголовок процедуры:
procedure TForm1.LoadQuestion;
begin
end;
Вы только что самостоятельно создали свою собственную процедуру, которую можно вызвать на выполнение из любого места программы (даже из другого модуля), в этой процедуре доступны все свойства и методы компонентов программы.
Пишем внутри этой процедуры обработку чтения одного вопроса из файла:
procedure TForm1.LoadQuestion;
Var Str_F: String; // временная строковая переменная для чтения данных из файла
begin
Inc(QCurrent); // увеличиваем порядковый номер текущего вопроса на 1 (равноценно команде QCurrent:=QCurrent+1)
// ЧИТАЕМ ВОПРОС
ReadLn(f,Str_F); // читаем вопрос из файла
Label1.Caption:=Str_F; // присваиваем компоненту текст вопроса
// ЧИТАЕМ ПЕРВЫЙ ОТВЕТ
ReadLn(f,Str_F); // читаем первый ответ из файла
Label2.Caption:=Str_F; // присваиваем компоненту текст ответа
ReadLn(f,Str_F); // читаем количество баллов за этот ответ
Button1.Tag:=StrToInt(Str_F); // присваиваем пользовательскому свойству Tag балл за этот ответ компоненту Button1
// ЧИТАЕМ ВТОРОЙ ОТВЕТ
ReadLn(f,Str_F);
Label3.Caption:=Str_F;
ReadLn(f,Str_F);
Button2.Tag:=StrToInt(Str_F);
// ЧИТАЕМ ТРЕТИЙ ОТВЕТ
ReadLn(f,Str_F);
Label4.Caption:=Str_F;
ReadLn(f,Str_F);
Button3.Tag:=StrToInt(Str_F);
// ЧИТАЕМ ЧЕТВЕРТЫЙ ОТВЕТ
ReadLn(f,Str_F);
Label5.Caption:=Str_F;
ReadLn(f,Str_F);
Button4.Tag:=StrToInt(Str_F);
end;
Возможно, вы еще не встречались со свойством Tag для компонентов. Это, можно сказать, пользовательское свойство. Оно имеет тип Integer, и ни на что конкретно в программе не влияет. Программист может его использовать в собственных целях, что иногда уменьшает количество объявляемых переменных в программе. Еще бывает удобно, когда компонент имеет в свойстве Tag какое-нибудь служебное число, как в нашем случае.
Создадим обработчик события для кнопок OnClick. Для всех кнопок это будет одна процедура и создается она стандартным способом, выбрав в инспекторе объектов компонент Button1, перейдя на страницу Events и щелкнув по полю OnClick, или можно дважды щелкнуть по самой кнопке Button1. Вот текст процедуры:
procedure TForm1.Button1Click(Sender: TObject);
Var i: Integer; // временная числовая переменная для выявления балла за отвеченный вопрос
s1: String; // временная переменная первого результата теста
p11,p12: Integer; // цифровой промежуток первого результата теста (минимальное число и максимальное)
s2: String;
p21,p22: Integer;
s3: String;
p31,p32: Integer;
s4: String;
p41,p42: Integer;
begin
i:=0; // сброс переменной
if Sender=Button1 then i:=Button1.Tag; // если была нажата кнопка Button1, то в i занести значение кол-ва баллов за этот ответ
if Sender=Button2 then i:=Button2.Tag;
if Sender=Button3 then i:=Button3.Tag;
if Sender=Button4 then i:=Button4.Tag;
QUser:=QUser+i; // увеличение общего количества баллов
if QCurrent=QCount then //если количество вопросов исчерпано, то
begin
ReadLn(f,p11); // чтение результатов и промежутков баллов из файла
ReadLn(f,p12);
ReadLn(f,s1);
ReadLn(f,p21);
ReadLn(f,p22);
ReadLn(f,s2);
ReadLn(f,p31);
ReadLn(f,p32);
ReadLn(f,s3);
ReadLn(f,p41);
ReadLn(f,p42);
ReadLn(f,s4);
if (QUser>=p11) and (QUser<=p12) then ShowMessage(s1); // выявление попадания в тот или иной промежуток и выдача результата теста
if (QUser>=p21) and (QUser<=p22) then ShowMessage(s2);
if (QUser>=p31) and (QUser<=p32) then ShowMessage(s3);
if (QUser>=p41) and (QUser<=p42) then ShowMessage(s4);
Close; // закрыть программу
end else LoadQuestion; // если кол-во вопросов не исчерпано, то прочитать из файла новый вопрос (переход к процедуре чтения)
end;
Далее, для того, чтобы эта процедура вызывалась при нажатии и на другие кнопки (Button2 - Button4) нужно для каждой отдельно выбранной кнопке в инспекторе объектов установить реакцию на событие OnClick из ниспадающего списка в Button1Click.
И последнее, для завершения работы надо корректно закрыть открытый нами ранее файл. Это делается с помощью события Form1Close (выбрать компонент Form1 и создать процедуру OnClose) с помощью признака открытого файла FOpen написать одну строку внутри процедуры:
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
if FOpen then CloseFile(f); // если файл был открыт, то закрыть его
end;
Ваша программа готова к работе.
Рассмотренную программу вместе с прилагаемым тестом можно забрать по этой ссылке (7,5КБ).
С уважением, ведущий уроков Semen semen@krovatka.net