LinuxForum.Fans
Тема

Программа для создания тестов. Обсуждение программы и кода

Автор: xwicked 40 сообщений 22350 просмотров
#184260

Здравствуйте!

Третья моя программа написана по наработкам второй(для создания электронных книг), поэтому содержит некоторые методы такого же названия например "openBookBase", что в последующих версиях будет исправлено на "openTestBase" :D

Программа представляет собой миникомплекс, включающий: создание, тестирование, отчёт(статистика). Версия 0.3.1 - исходник + сборки.

Скрин последней версии:


Ссылка на SourceForge.net. Код под GNU GPL v2 - OpenSource. С++(Qt).

Обсуждаем.

#184267

Не совсем в тему.
мое личное имхо: вы слишком распыляетесь по программам. Доведите до красивого состояния тот же кроссворд.

#184273
voral написал:
Не совсем в тему.
мое личное имхо: вы слишком распыляетесь по программам. Доведите до красивого состояния тот же кроссворд.

Полностью согласен. Грамотного программиста отличают красивый код, логичность и продуманность ПО, ну и конечно же завершенность ПО.
Бесмысленно расплываться между проектами, так и не доведя ни один из них до конца.

#184284
Michail_Ul написал:
[quote=voral]Не совсем в тему.
мое личное имхо: вы слишком распыляетесь по программам. Доведите до красивого состояния тот же кроссворд.

Полностью согласен. Грамотного программиста отличают красивый код, логичность и продуманность ПО, ну и конечно же завершенность ПО.
Бесмысленно расплываться между проектами, так и не доведя ни один из них до конца.[/quote]
Я здесь не для того, чтобы себя показать, а найти помощников в лице всего Свободного Сообщества. Ну а темы я думаю неплохие. wink :P :rolleyes:

ЗЫ: Sun StarOffice -> Sun OpenOffice -> Sun ??? -> Oracle! Так что, всё нужно делать вовремя...

#184456
xwicked написал:
Я здесь не для того, чтобы себя показать, а найти помощников в лице всего Свободного Сообщества. Ну а темы я думаю неплохие

Да только видя не серьёзное отношение автора к проекту, особо даже и смотреть желание пропадает.

При чем тут офис и его исторический путь?

#184470
voral написал:

Да только видя не серьёзное отношение автора к проекту, особо даже и смотреть желание пропадает.

При чем тут офис и его исторический путь?

В сети множество похожих проектов. Автор набросал свои идеи, выложил в сеть и надеется, что люди тут-же объединятся и напишут программу.
Если и вы на это надеетесь, то вам нужно идти в руководители проектов, но никак не в программисты.
Поймите простую вещь, у большинства людей семьи, дети. Много работы и других забот. При этом свободного времени нехватает,
и они не станут его тратить на столь несерьезные вещи.
Час работы хорошего и толкового программиста стоит дорого. И этот программист знает себе цену.
Все хорошие специалисты работают на крупные проекты, я сомневаюсь что столь несерьезный подход их заинтересует.

#184473

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

ЗЫ: Последняя текущая версия 0.4.4 - потом будут Ваши серьёзные комментарии.

#184506

xwicked, а как вы подсчитываете оценку? (пару месяцев назад на одно форуме по программированию была целая тема этому вопросу посвящена) - если нужно попробую найти. Там много "интересного" всплыло.

Michail_Ul написал:
Если и вы на это надеетесь, то вам нужно идти в руководители проектов, но никак не в программисты.

Как то странно ваш пост построен - вроде бы ответ мне, вроде против, хотя и в том же духе :) Ну да ладно не о том сейчас.

#184537
voral написал:
xwicked, а как вы подсчитываете оценку? (пару месяцев назад на одно форуме по программированию была целая тема этому вопросу посвящена) - если нужно попробую найти. Там много "интересного" всплыло.


[quote=Michail_Ul]Если и вы на это надеетесь, то вам нужно идти в руководители проектов, но никак не в программисты.

Как то странно ваш пост построен - вроде бы ответ мне, вроде против, хотя и в том же духе :) Ну да ладно не о том сейчас.[/quote]

Voral Это ответ не тебе а xwicked. Видимо не очень понятно у меня получилось. :D

#184543

Описание: кроссплатформенная программа для создания и редактирования тестов, с последующим тестированием.
Её могут использовать все, кому понадобиться быстро создать тест и проверить знания. Можно будет создать вопрос типа "Что показано на изображении" и использовать как в школе так и на предприятии, для сдачи квалификационных экзаменов. Все тесты хранятся в базе данных SQLite3. Это позволяет располагать файл базы на любом сервере(Windows, GNU / Linux, и т. д.), без необходимости установки сервера баз данных.

Всё находится в одном исполняемом файле. Для редактирования / тестирования, необходимо переключить соответствующий пункт из меню "Режим". Отчёт о протестировавшихся представлен статистикой.

Внутренняя архитектура: список вопросов хранится в QStringList ListQuestions, список правильных ответов в QStringList ListRightAnswer, варианты ответа при тестировании в QStringList ListCheckRightAnswer. Изображения к тесту находятся в QByteArray ListImages[100000]. Режим редактирования защищён паролем, который пока что, хранится в открытом виде.

#188199

Здравствуйте!

Вот новая версия 0.3.5.

Теперь программа предоставляет возможность создавать тесты, используя 4-е новых типа вопроса: много ответов, ввод слова(предложения) с клавиатуры, ввод числа с клавиатуры, установка последовательности ответов.

Хочу обратить Ваше внимание на то, что в данной версии я сделал вот так:

text
         if(slotSelectTypeQuestion() == false) return;

if(sTypeQuestion == tr("One answer")) setOneAnswerType();

if(sTypeQuestion == tr("Many answers")) setManyAnswerType();

if(sTypeQuestion == tr("Installation sequence")) setInstallSequenceType();

if(sTypeQuestion == tr("Enter number")) setEnterNumberType();

if(sTypeQuestion == tr("Enter word (sentence)")) setEnterWordType();

slQuestionType << sTypeQuestion; SaveState = false;

Функция slotSelectTypeQuestion() отвечает за вывод списка, из которого можно будет выбрать соответствующий тип вопроса.
Далее идёт проверка типа и его применение - функция начинается на "set<Тип_вопроса>". Потом выбранный вариант загоняется в список типов QStringList slQuestionType. Но, здесь есть одно "но":

text
sTypeQuestion == tr("One answer")

- тип вопроса взят в "tr()", так что тест, составленный на русском языке не будет работать при использовании английского языка интерфейса программы и наоборот :( . Просьба, перед сборкой устранить эту ошибку, так как мной она будет устранена в последующий версиях :) .

Дабы быть уверенным в том, что файлы не побились, при закачке, я добавил текстовик с md5-контрольными суммами.
Git-репозиторий:

git://shelk-test.git.sourceforge.net/gitroot/shelk-test/shelk-test (read-only)

P.S. Ещё один очень важный момент: версии 0.3.5, 0.4.1, 0.4.2 - сборки являются минимальными. То есть, чтобы посмотреть на все функции, необходимо программу собрать из исходника :) . Это делалось для проверки аудитории пользователей на заинтересованность в программе, путем отправки мне запроса на получение исходного кода. Что было не очень удачной идеей и, с версии 0.4.3, сборки программы уже снова полнофункциональны. Прошу извинить меня за данное неудобство.

#188265
text

 if(sTypeQuestion == tr("Enter word (sentence)")) setEnterWordType();

Имхо. Это ужос.

xwicked написал:
- тип вопроса взят в "tr()", так что тест, составленный на русском языке не будет работать при использовании английского языка интерфейса программы и наоборот sad . Просьба, перед сборкой устранить эту ошибку, так как мной она будет устранена в последующий версиях smile .

Имхо. Здесь гораздо больше потенциальных проблем.

Вы сравниваете строковые переменные, которые могут быть автоматически заменены на перевод. При переводе это будут две разных строки (в разных местах) и если переводчику покажется логичным перевести чуть чуть, но по разному. Или сделает лишний пробел, или опечатку, или вместо круглых скобок поставит, что то другое..... Короче слишком много если.
Опять же, на сколько я понял ваш подход "sTypeQuestion" и остальные переменные считываются из конфига. Т.е. могут быть исправлены руками в конфиге, на непонятно, что. Более того, если англоязычный поделится своим конфигом с русскоязычный до будет все плохо...
Если, вдруг, лет через 10 вы решите, что есть более логичная фраза нежели "Enter word (sentence)", то вы окажетесь заложником совместимости.... Мешать интерфейс и логику - это жуть.

#188354

voral написал:
... Опять же, на сколько я понял ваш подход "sTypeQuestion" и остальные переменные считываются из конфига. ...
sTypeQuestion - это переменная, которая является частью одного создаваемого теста. То бишь, сколько тестов в базе данных будет, столько раз будет сохранено содержимое данной переменной в базу. При этом текст из переменной sTypeQuestion используется и для отображения данного типа в метке
text
lTypeQuestion.setText(sTypeQuestion);

ЗЫ: На этом наглядном примере, я понял, что такое смешать интерфейс с логикой oO :lol: А хотел я, всего лишь, сэкономить время...

#200730

Для распространения программы между RPM-дистрибутивами, воспользуйтесь следующим спекфайлом:

Summary: On Qt-based, cross-platform software is designed for creating and editing tests, followed by tests of knowledge.

Summary(ru): Кроссплатформенная программа для создания и редактирования тестов, с последующим тестированием. Основана на Qt.

%define version 0.3.5

Group: Education

Name: shelk-test

BuildRequires: gcc-c++, libqt4-sql-sqlite >= 4.4.3, libqtgui4 >= 4.4.3, libqt4-core >= 4.4.3
BuildArch: %ix86

Provides: shelk-test

Release: alt1

Source: shelk-test-%{version}.tar.gz

Url: http://labfreetech.org/index_e.html

Version: %{version}

License: GPLv2

Packager: LabFreeTech.org admin@labfreetech.org

%description
On Qt-based, cross-platform software is designed for creating and editing tests, followed by tests of knowledge.
Supported features: - Creation of a test; - Editing of the test; - Testing; - Adding / removing tests from the database; - Rich editing the test of the tests; - Password protection regime editing;

%description -l ru
Кроссплатформенная программа для создания и редактирования тестов, с последующим тестированием. Основана на Qt.
Поддерживаемые функции: - Создание теста; - Редактирование теста; - Тестирование; - Добавление / удаление тестов из базы данных; - Расширенное редактирование текста вопросов теста; - Защита паролем режима редактирования;

%prep

%setup -q

%build
%make_build

%install

cp -Rv shelk/ %buildroot/

%clean

rm -rf $RPM_BUILD_ROOT

%files

/usr/bin/*
/usr/share/*

%post

test -e /usr/bin/xwts-0.3.5 || ln -s /usr/bin/xwts-0.3.5 /usr/bin/shelk-test-0.3.5

if (test -d $HOME/Desktop) then
test -e $HOME/Desktop/xwts.desktop || cp -r /usr/share/applications/xwts.desktop $HOME/Desktop/shelk-test.desktop
else
echo "Use command \"/usr/bin/xwts-0.3.5\""
fi

%postun

test -L /usr/bin/shelk-test-0.3.5 && rm -r /usr/bin/shelk-test-0.3.5
test -e $HOME/Desktop/shelk-test.desktop && rm -r $HOME/Desktop/shelk-test.desktop
Но, в нём есть небольшая ошибка или, даже скорей недочёт:
BuildRequires: gcc-c++, libqt4-sql-sqlite >= 4.4.3, libqtgui4 >= 4.4.3, libqt4-core >= 4.4.3
Непонятно почему при установке собранного пакета с этим спекфайлом, происходит игнорирование выделенной библиотеки :( , но libqt4-sql устанавливается, хотя явного указания её нет. Кто знает причину, прошу ответить.
Этот спекфайл находится в архиве shelk-test-0.3.5-rpm, расположенный по ссылке в предыдущем посте. Там же находится архив исходного кода и для дитрибутивов GNU / Linux Debian(/Ubuntu), с пометкой "deb".
#203524

Продолжаем знакомиться с кодом. Версия 0.4.1 Включает в себя следующие изменения:

- Исправлена ошибка в реализации типа вопроса "Ввод числа";
- Исправлена ошибка в реализации типа вопроса "Установка последовательности";
- Добавлена возможность вставки изображения в вопрос теста;
- Добавлены горячие клавиши;
- Добавлена возможность использования баллов;
- Добавлена возможность ввода имени тестируемого;
- Добавлена статистика.

Статистика - это первый вариант журнала отчёта о протестировавшихся студентах, представлена обычным html-файлом:

text
            QFile file("/tmp/shelk-test-0.4.1/Stat.html");
            QDataStream sfile(&file);
            if(!file.exists())
            {
                file.open(QIODevice::WriteOnly);
                strStat.clear();
                strStat.append("<HTML>\n"
                               " <BODY>\n"
                               "  <TABLE border=\"3\">\n"
                               "   <TR>\n"
                               "    <TD> " + tr("User name") + " </TD>\n"
                               "    <TD> " + tr("Name test") + " </TD>\n"
                               "    <TD> " + tr("Total questions") + " </TD>\n"
                               "    <TD> " + tr("Correct answers") + " </TD>\n"
                               "    <TD> " + tr("Wrong answers") + " </TD>\n"
                               "    <TD> % </TD>\n"
                               "    <TD> " + tr("Total points:") + "  </TD>\n"
                               "    <TD> " + tr("Date") + "  </TD>\n"
                               "    <TD> " + tr("Time end") + "  </TD>\n"
                               "   </TR>\n"
                               "   <TR>\n"
                               "    <TD>" + UserName + "</TD>\n"
                               "    <TD>" + StatNameTest + "</TD>\n"
                               "    <TD>" + sTotalQuestions + "</TD>\n"
                               "    <TD>" + sCorrectAnswer + "</TD>\n"
                               "    <TD>" + sWrongAnswer + "</TD>\n"
                               "    <TD>" + strPercent + "</TD>\n"
                               "    <TD>" + sAllPoint + "</TD>\n"
                               "    <TD>" + strDate + "</TD>\n"
                               "    <TD>" + strTime + "</TD>\n"
                               "   </TR>\n"
                               "  </TABLE>\n"
                               " </BODY>\n"
                               "</HTML>\n");
                sfile << strStat;
                file.close();
                FormStat.StatLabel->setText(strStat);
            }

Есть что-нибудь проще или для начала сойдёт?

#203678
xwicked написал:
Есть что-нибудь проще или для начала сойдёт?

На безрыбье.
Гибче: внешний файл - шаблон, свои теги при формировании заменяешь. В итоге каждый может нарисовать под себя.

#203742

voral написал:
...
Гибче: внешний файл - шаблон, свои теги при формировании заменяешь. В итоге каждый может нарисовать под себя.
Понял, в последующих версиях сделаю возможность выбора полей для формирования статистики.
#207526

Здравствуйте! Программа продолжает совершенствоваться и версия 0.4.2 включает 25 изменений. Было изменено отображение картинки в тесте. Теперь используется рисование непосредственно на виджите:

text
//WidgetImage.cpp

//Передаётся в конструктор вторым параметром путь к загружаемому изображению WidgetImage::WidgetImage(QWidget* pwgt/*=0*/, QString sFileName) : QWidget(pwgt) { //Копирование пути в глобальную переменную, //для использования в обработчике события рисования str1 = sFileName; }

/*virtual*/ void WidgetImage::paintEvent(QPaintEvent*) { QImage img(str1);//Загрузка изображения QPainter painter;

painter.begin(this); //На img2 рисуется изображение из img масштабируемо по высоте QImage img2 = img.scaledToHeight(height()); painter.drawImage(0, 0, img2);//Отрисовка на виджите painter.end(); }

//TestSystem.cpp TestSystem::TestSystem(QMainWindow *pwgt /* = 0*/): QMainWindow(pwgt) { setupUi(this); ... QSplitter* splitEditHV;//Объявление разделителя ... splitEditHV = new QSplitter(Qt::Vertical);//Создание разделителя вертикально ... //Создание виджита в разделителе с передачей загружаемого изображения widgetImage = new WidgetImage(splitEditHV, "/tmp/shelk-test-0.4.2/temp/tempimage"); widgetImage->resize(300, 300);//Изменение размера виджета ... }

Как я предполагаю, что есть проще и правильнее способ, но времени найти его нет :( прошу помочь.

Ещё интересует вопрос по отлавливанию ошибок try catch. Это первая реализация. Что кроме вынесения в один метод обработчиков, во избежании наличия копированного кода, там можно сделать? Благодарю! :)

Ещё изменения:


- Добавлен учёт времени тестируемого;
- Добавилась возможность ручного запуска теста, по нажатию на клавишу;
- Добавлена возможность остановки теста, с занесением информации о тестируемом в статистику;
- Добавилась возможность экспорта в файл XWT;
- Добавилась возможность импорта из файла XWT;
#224807

0.4.3 - это новая версия программы.

Здравствуйте! :)

- Добавлено шифрование и сжатие экспортируемого файла теста в формате xwt;
- Исправлена ошибка, когда пароль режима программы хранился и проверялся в открытом виде;
- Добавлена возможность работать с несколькими хранилищами тестов;
- Добавлена возможность резервировать хранилище тестов(со сжатием до 5-ти раз(!));
- Добавлена возможность запроса на сохранение теста, при его закрытии;
text
    QString RegimePassword;//Пароль режима
    QByteArray  baPassword;//Хеш пароля;
    RegimePassword = "Password";
    baPassword.append(RegimePassword);
    baPassword = baPassword.toBase64();
    baPassword = qCompress(baPassword, 9);
    baPassword = baPassword.toBase64();
    baPassword = qCompress(baPassword, 9);
    baPassword = baPassword.toBase64();
    RegimePassword.clear();
    RegimePassword.append(baPassword);

Для шифрования используется алгоритм Base64, для сжатия функция qCompress. Этот код может быть реверсирован и получен изначальный пароль фактически в 100%-ом виде. В будущем он будет заменён на md5 хеш или похожий.

А вот следующая проблема - это шифрование самого теста. Алгоритмы те же. При наличии у студента(/пользователя) кода все тесты предстают в абсолютно первозданном виде :( .
Шифрование по ключу думаю будет слишком хорошая защита, которую мне одному не реализовать. Найти другой вариант за ~2-а месяца у меня не получилось.

Какие есть варианты? Благодарю!
ЗЫ: Всего 25 изменений.

#227185

0.4.4. Исправлено 15 ошибок(!). Появились 2-е структуры:

text
//Структура файла свойств
struct sFileSettings
{
     bool gRegimeViewing,//true - Режим просмотра, false - Режим редактирования
  LanguageProgram,//Язык программы
  bStandardPanel,//Состояние показа стандартной панели
  bEditPanel,//Состояние показа панели редактирования
  bFormatPanel,//Состояние показа панели форматирования
  bTimePanel,//Состояние показа панели времени
  bPointPanel,//Состояние показа панели баллов
  bLimitTime,//Ограничение времени
  bAutoStartTest;//Автоматический запуск
     QString RegimePassword;//Пароль режима
};

//Структура теста struct sShelkTestVar { QString sNameTest,//Название теста sAllPoint,//Все баллы строкой sMidPoint,//Средний балл строкой sCurrentPoint,//Текущий балл строкой sTypeQuestion,//Тип вопроса TestPassword,//Пароль теста StatNameTest,//Имя пользователя в статистике sQuestion,//Текст вопроса sTimeStart;//Время начала

int CountQuestion,//Количество вопросов iNumberQuestion,//Номер вопроса iNumberTest,//Номер теста AllPoint,//Общий балл MidPoint,//Средний балл CurrentPoint,//Текущий балл AllPointCheck,//Общий балл для проверки AllTime,//Общее время MidTime,//Среднее время CurrentTime,//Текущее время i5First;//Номер прошлого выделенного ответа (установка последовательности)

QVector<double> *iRangeFirst,//Начальное значение диапазона (ввод числа) *iRangeLast;//Конечное значение диапазона (ввод числа)

QStringList ListQuestions,//Список вопросов slTableNumber,//Список номеров таблиц в базе тестов slQuestionType,//Список типов вопросов slPoint,//Список баллов slTime;//Список времени

QVector<QVariant> *ListRightAnswer,//Список правильных ответов *ListCheckRightAnswer;//Список правильных ответов для проверки

QVector<QStringList> *ListAnswers;//Список ответов QVector<QByteArray> *ListImages;//Список изображений QVector<QFont> *ListFonts;//Список шрифтов QSqlDatabase db;//База данных тестов

QTimer TimerTest, TimerTestLabel; QTime TimeTestCurrent, TimeTestAll; };

Их было решено использовать сразу, после выхода кроссворда за предел стека :lol:
Введение вектора типа QVariant позволило функцию проверки ответов максимально упростить и вынести отдельно:

text
bool ShelkTest::MakeCheckRightAnswer(int iIndex)
{
    int iTypeR;
    QString str1, str2;
    QByteArray ba1, ba2;

iTypeR = vShelkTestVar->ListRightAnswer->at(iIndex).type();

if(iTypeR == QVariant::Int) { if(vShelkTestVar->ListRightAnswer->at(iIndex).toInt() == vShelkTestVar->ListCheckRightAnswer->at(iIndex).toInt()) return true; else return false; }

if(iTypeR == QVariant::Double) { if(vShelkTestVar->ListRightAnswer->at(iIndex).toDouble() == vShelkTestVar->ListCheckRightAnswer->at(iIndex).toDouble()) return true; else return false; }

if(iTypeR == QVariant::String) { if(vShelkTestVar->ListRightAnswer->at(iIndex).toString() == vShelkTestVar->ListCheckRightAnswer->at(iIndex).toString()) return true; else return false; }

if(iTypeR == QVariant::ByteArray) { ba1.append(vShelkTestVar->ListRightAnswer->at(iIndex).toByteArray()); ba2.append(vShelkTestVar->ListCheckRightAnswer->at(iIndex).toByteArray()); str1.append(vShelkTestVar->ListRightAnswer->at(iIndex).toByteArray()); str2.append(vShelkTestVar->ListCheckRightAnswer->at(iIndex).toByteArray());

if(vShelkTestVar->ListRightAnswer->at(iIndex).toByteArray() == vShelkTestVar->ListCheckRightAnswer->at(iIndex).toByteArray()) return true; else return false; }

if(iTypeR == QVariant::StringList) { if(vShelkTestVar->ListRightAnswer->at(iIndex).toStringList() == vShelkTestVar->ListCheckRightAnswer->at(iIndex).toStringList()) return true; else return false; } return false; }

Несмотря на то, что QVariant не рекомендуется использовать из за большого потребления памяти, так как он позволяет хранить даже контейнеры(!), я решил сделать это. Использование один раз в масштабах простой программы для создания тестов показала свою рентабельность.
Скачать

Войдите или зарегистрируйтесь, чтобы ответить.