Здравствуйте!
Третья моя программа написана по наработкам второй(для создания электронных книг), поэтому содержит некоторые методы такого же названия например "openBookBase", что в последующих версиях будет исправлено на "openTestBase"
Программа представляет собой миникомплекс, включающий: создание, тестирование, отчёт(статистика). Версия 0.3.1 - http://sourceforge.net/projects/shelk-test/files/0.3.1/.
Скрин последней версии:
http://sourceforge.net/projects/shelk-test на SourceForge.net. Код под GNU GPL v2 - OpenSource. С++(Qt).
Обсуждаем.
xwicked, а как ею пользоваться?
Описание: кроссплатформенная программа для создания и редактирования тестов, с последующим тестированием.
Её могут использовать все, кому понадобиться быстро создать тест и проверить знания. Можно будет создать вопрос типа "Что показано на изображении" и использовать как в школе так и на предприятии, для сдачи квалификационных экзаменов. Все тесты хранятся в базе данных SQLite3. Это позволяет располагать файл базы на любом сервере(Windows, GNU / Linux, и т. д.), без необходимости установки сервера баз данных.
Всё находится в одном исполняемом файле. Для редактирования / тестирования, необходимо переключить соответствующий пункт из меню "Режим". Отчёт о протестировавшихся представлен статистикой.
Внутренняя архитектура: список вопросов хранится в QStringList ListQuestions, список правильных ответов в QStringList ListRightAnswer, варианты ответа при тестировании в QStringList ListCheckRightAnswer. Изображения к тесту находятся в QByteArray ListImages[100000]. Режим редактирования защищён паролем, который пока что, хранится в открытом виде.
Здравствуйте!
Вот http://sourceforge.net/projects/shelk-test/files/0.3.5/ 0.3.5.
Теперь программа предоставляет возможность создавать тесты, используя 4-е новых типа вопроса: много ответов, ввод слова(предложения) с клавиатуры, ввод числа с клавиатуры, установка последовательности ответов.
Хочу обратить Ваше внимание на то, что в данной версии я сделал вот так:
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;
sTypeQuestion == tr("One answer")
- тип вопроса взят в "tr()", так что тест, составленный на русском языке не будет работать при использовании английского языка интерфейса программы и наоборот . Просьба, перед сборкой устранить эту ошибку, так как мной она будет устранена в последующий версиях . Как Вы решили вопрос с безопасностью? SQLite по умолчанию ничем не защищена, и вытащить из нее все ответы - плёвое дело.
Немного оффтоп
Для распространения программы между RPM-дистрибутивами, воспользуйтесь следующим спекфайлом:
Продолжаем знакомиться с http://sourceforge.net/projects/shelk-test/files/0.4.1/. Версия 0.4.1 Включает в себя следующие изменения:
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);
}
Есть что-нибудь проще или для начала сойдёт?
Здравствуйте! Программа продолжает совершенствоваться и http://sourceforge.net/projects/shelk-test/files/0.4.2/ включает 25 изменений. Было изменено отображение картинки в тесте. Теперь используется рисование непосредственно на виджите:
//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);//Изменение размера виджета
...
}
Как я предполагаю, что есть проще и правильнее способ, но времени найти его нет прошу помочь. 0.4.3 - это новая версия программы.
http://sourceforge.net/projects/shelk-test/files/0.4.3/!
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 хеш или похожий.0.4.4. Исправлено 15 ошибок(!). Появились 2-е структуры:
//Структура файла свойств
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;
};
Их было решено использовать сразу, после выхода кроссворда за предел стека 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 не рекомендуется использовать из за большого потребления памяти, так как он позволяет хранить даже контейнеры(!), я решил сделать это. Использование один раз в масштабах простой программы для создания тестов показала свою рентабельность.struct sFileSettings
{
bool gRegimeViewing;//true - Режим просмотра, false - Режим редактирования
bool LanguageProgram;//Язык программы
bool bStandardPanel;//Состояние показа стандартной панели
bool bEditPanel;//Состояние показа панели редактирования
bool bFormatPanel;//Состояние показа панели форматирования
bool bTimePanel;//Состояние показа панели времени
bool bPointPanel;//Состояние показа панели баллов
bool bLimitTime;//Ограничение времени
bool bAutoStartTest;//Автоматический запуск
QString RegimePassword;//Пароль режима
//КОНСТРУКТОР ОБЯЗАТЕЛЬНО
sFileSettings()
{
gRegimeViewing=false;
LanguageProgram=false;
bStandardPanel=false;
bEditPanel=false;
bFormatPanel=false;
bTimePanel=false;
bPointPanel=false;
bLimitTime=false;
bAutoStartTest=false;
}
};
struct sFileSettings
{
union
{
UINT32 m_AllFlags;
struct
{
UINT32 gRegimeViewing :1;//true - Режим просмотра, false - Режим редактирования
UINT32 LanguageProgram :1;//Язык программы
UINT32 bStandardPanel :1;//Состояние показа стандартной панели
UINT32 bEditPanel :1;//Состояние показа панели редактирования
UINT32 bFormatPanel :1;//Состояние показа панели форматирования
UINT32 bTimePanel :1;//Состояние показа панели времени
UINT32 bPointPanel :1;//Состояние показа панели баллов
UINT32 bLimitTime :1;//Ограничение времени
UINT32 bAutoStartTest :1;//Автоматический запуск
UINT32 :23;//резерв
};
};
QString RegimePassword;//Пароль режима
sFileSettings()
{
m_AllFlags=0;
}
};
Алексей1153, благодарю, но конструкторы я буду осваивать для классов, с приходом полного ООП в эту программу
xwicked, да не, начинай уже осваивать ) Потом же исправлять и глюки вылавливать придётся.
Открою маленький секрет: для компилятора C++ нет разницы, class это, struct или union - у них всех всё одинаково работает, они имеют конструктор и деструктор. Тонкости в различии : union и struct по умолчанию устанавливают доступ public своему содержимому, а class - private. Ну и в union у объединённых переменных вроде не может быть явного конструктора/деструктора, но это уже к нашему случаю не относится, так как это внутренние данные, а не сама структура.
Здравствуйте! Представляю новую версию программы 0.4.7. Добавился файл с константами:
//const.h
//Определение путей констант, в соответствии с системой
#if defined(Q_WS_WIN)
#define PATH_TMP_SET QApplication::applicationDirPath() + "/settings.xwst"
#define PATH_SHARE_DOC QApplication::applicationDirPath()
#define PATH_SHARE_APP QApplication::applicationDirPath()
#define PATH_TMP QApplication::applicationDirPath()
#elif defined(Q_WS_X11)
#define PATH_TMP_SET "/tmp/shelk-test-0.4.7/settings.xwst"
#define PATH_SHARE_DOC "/usr/share/doc/shelk-test-0.4.7"
#define PATH_SHARE_APP "/usr/share/shelk-test-0.4.7"
#define PATH_TMP "/tmp/shelk-test-0.4.7"
#elif defined(Q_WS_MAC)
#define PATH_TMP_SET QApplication::applicationDirPath() + "/settings.xwst"
#define PATH_SHARE_DOC QApplication::applicationDirPath()
#define PATH_SHARE_APP QApplication::applicationDirPath()
#define PATH_TMP QApplication::applicationDirPath()
#endif
#define MODE_VIEW true
#define MODE_EDIT false
#define LANGUAGE_RUSSIAN true
#define LANGUAGE_ENGLISH false
#define TOOLBAR_SHOW true
#define TOOLBAR_HIDE false
#define STATISTICS_FIELD_SHOW true
#define STATISTICS_FIELD_HIDE false
#define VIEW_RUSSIAN 0
#define VIEW_ENGLISH 1
#define EDIT_RUSSIAN 2
#define EDIT_ENGLISH 3
//Типы вопросов
#define TYPE_ONE_ANSWER "1"
#define TYPE_MANY_ANSWERS "2"
#define TYPE_ENTER_WORD "3"
#define TYPE_ENTER_NUMBER "4"
#define TYPE_INSTALLATION_SEQUENCE "5"
//Структура показа полей статистики
struct TStateFieldStatistics
{
bool bUserName,//Имя пользователя
bNameTest,//Название теста
bTotalQuestions,//Всего вопросов
bCorrectAnswers,//Правильных ответов
bWrongAnswers,//Неправильных ответов
bTotalPoints,//Всего баллов
bScore,//Оценка
bDate,//Дата
bTimeBegin,//Время начала
bTimeEnd,//Время окончания
bElapsedTime;//Прошедшее время
};
Оптимизирована функция изменения шрифта(была 195 строк): bool Accept;
QFont TextFont;
TextFont = TextQuestion->textCursor().charFormat().font();
TextFont = QFontDialog::getFont(&Accept, TextFont);
if(Accept)
{
QTextCharFormat tcf;
tcf.setFont(TextFont);
TextQuestion->textCursor().setCharFormat(tcf);
cbFont.setCurrentFont(TextFont);
}
else return 1;
http://sourceforge.net/projects/shelk-test/files/0.4.7/
Новая и последняя на данный момент версия 1.0.0. Здесь я постарался конкретно. Изменения:
//Новые константы
//Типы вопросов для определения содержимого переменной QVariant
#define V_TYPE_ONE_ANSWER QVariant::Int
#define V_TYPE_MANY_ANSWERS QVariant::ByteArray
#define V_TYPE_ENTER_WORD QVariant::String
#define V_TYPE_ENTER_NUMBER QVariant::Double
#define V_TYPE_INSTALLATION_SEQUENCE QVariant::StringList
//Перевод слов в rtf-unicode
QString ShelkTest::WordToUnicode(QString sWord)
{
QString sTemp,
sNum;
sTemp.clear();
for (int i = 0; i < sWord.length(); i++)
{
sNum.setNum(sWord.at(i).unicode());
sTemp.append("\\u" + sNum + "?");
}
return sTemp;
}
private: QWebView *wvUpdate;//Для обновления и печати :)
, так как QTextEdit и QTextBrowser не могут отображать элементы ввода(текст, переключатель, флажок), для формирования правильной html-ки. Раз уж было принято решение добавить новую зависимость от библиотеки QtWebkit, то на его же основе я сделал проверку наличия новой версии через интернет, смотрим:void ShelkTest::slotCheckForUpdates()
{
connect(wvUpdate, SIGNAL(loadFinished(bool)), SLOT(slotMakeCheckUpdates(bool)));
wvUpdate->load(QUrl("http://labfreetech.org/shelk_1.0.0.html"));
}
void ShelkTest::slotMakeCheckUpdates(bool bError)
{
if (bError && wvUpdate->page()->findText("new_version")) QMessageBox::information(0, tr("Information"), tr("On the official website available a new version!"));
else QMessageBox::information(0, tr("Information"), tr("You have the latest version!"));
disconnect(wvUpdate, SIGNAL(loadFinished(bool)), 0, 0);
}
Как видно - всё просто. Если текст "new_version" найден в загруженной html-ке, то есть новая версия http://sourceforge.net/projects/shelk-test/files/1.5.2
По-тихоньку идёт совершенствование программы. И сейчас от основного кода был отделён модуль Тестирование.
Пршу протестировать на наличие ошибок первую бета-версию: http://sourceforge.net/projects/shelk-test/files/1.6.2/. Для полноценной проверки может потребоваться прошлая версия http://sourceforge.net/projects/shelk-test/files/1.5.2/.
Благодарю за ответы!
если сделаешь возможность компиляции в exe, цены тебе не будет)
Форум Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)