DIPLOM (1204343), страница 8
Текст из файла (страница 8)
{
}
const auto& getQuestion() const { return question; }
const auto& getAnswer() const { return correct_answer; }
const auto& getAnswerVariants() const { return answers_list; }
bool operator == (const QuestionFileT& rhs) const
{
return question==rhs.question;
}
operator void*() const {
return (void*)(question.size()
&& correct_answer.size()
&& answers_list_is_correct()
? 1 : 0);
}
void setAnswer(auto&& str) { correct_answer = str; }
void setQuestion(auto&& str) { question = str; }
private:
bool answers_list_is_correct() const
{
for (const auto& it : answers_list)
if (!it.size())
return false;
return true;
}
};
Q_DECLARE_METATYPE(QuestionFileT)
Q_DECLARE_METATYPE(TestResult)
// возврат объекта с вопросом и ответами
inline
auto makeQuestionFileT(int id, const QString& testName)
{
QFile f(make_file_name(id, testName));
if (!f.open(QFile::ReadOnly|QFile::Text)) {
return QuestionFileT{};
}
std::vector<QString> answers_list;
QString question = f.readLine();
QString correct_answer = f.readLine();
while (f.canReadLine())
answers_list.push_back(QString(f.readLine()).simplified());
return QuestionFileT{question.simplified(), correct_answer.simplified(), answers_list};
}
//путь к файлу, возвращает id вопроса
inline
int getFileId(const QString& fPath)
{
auto arg = normalize_path(QString(fPath)).toStdString();
auto pos_first = arg.find_last_of("/");
if (pos_first != std::string::npos)
{
auto second_pos = arg.find(".", pos_first);
if (second_pos != std::string::npos)
return std::stoi(std::string(arg, pos_first+1, second_pos-pos_first-1));
}
return 0;
}
#include <QSettings>
// опции теста, выбор времени отводимого на тест
class TestSettings {
static auto getFileAddr(const QString &tName) {
return QString(QUESTIONS_DIR) + tName + "/settings.ini";
}
bool set(const QString &tName, auto &&key, auto &&value)
{
QSettings sets(getFileAddr(tName), QSettings::IniFormat);
sets.setValue(key, value);
return sets.status() == QSettings::NoError;
}
public:
auto getTestTime(const QString &tName) const {
QSettings sets(getFileAddr(tName), QSettings::IniFormat);
return sets.value("test_time", 60*15).toUInt();
}
auto setTestTime(const QString &tName, uint value) {
return set(tName, "test_time", value);
}
};
// хранение теста
class QustionListT {
QString testName;
uint testTime;//seconds
std::map<int, QuestionFileT> QuestionList;
public:
QustionListT(const QString& testName)
{
this->testName = testName;
loadAll(testName);
testTime = TestSettings().getTestTime(testName);
}
QustionListT() = default;
~QustionListT() = default;
QustionListT& operator=(const QustionListT&) = default;
QustionListT& operator=(QustionListT&&) = default;
QustionListT(const QustionListT&) = default;
QustionListT(QustionListT&&) = default;
auto size() const { return QuestionList.size(); }
QuestionFileT operator[](int index) const
{
if (index >= (int)QuestionList.size())
return QuestionFileT();
auto it = QuestionList.rbegin();
for (int i=0; i<index; i++, it++)
;
return it->second;
}
auto currentTestName() const {
return testName;
}
auto getTimeLeft() const {
return this->testTime;
}
void currentTestName(auto&& testName) {
this->testName = testName;
}
auto maxId() const
{
return QuestionList.size() ? QuestionList.rbegin()->first : 0;
}
bool addNew(const QuestionFileT& qst)
{
auto newId = maxId() + 1;
if (saveOne(newId, qst)) {
QuestionList.insert({newId, qst});
return true;
}
return false;
}
void edit(const QuestionFileT& oldName, const QuestionFileT& newName)
{
for (auto& it : QuestionList)
{
if (it.second == oldName) {
if (saveOne(it.first,newName))
it.second = newName;
break;
}
}
}
private:
auto find(const QuestionFileT& oldName)
{
for (auto it=QuestionList.begin(); it!= QuestionList.end(); ++it) // auto it : QuestionList
if (it->second == oldName)
return it;
return QuestionList.end();
}
public:
bool del(const QuestionFileT& oldName)
{
auto it = find(oldName);
if (it == QuestionList.end())
return false;
if (remove_file(it->first, testName)) {
QuestionList.erase(it);
return true;
}
return false;
}
private:
bool insert(int id, const QuestionFileT& qst)
{
QuestionList.insert({id, qst});
return true;
}
bool loadAll(const QString& testName)
{
QStringList all_files;
QDirIterator it(make_dir_name(testName), QStringList() << "*.txt", QDir::Files);
while (it.hasNext())
all_files << it.next();
foreach(QString file_path, all_files)
{
auto fId = getFileId(file_path);
if (fId > 0) {
if (auto qst = makeQuestionFileT(fId, testName)) {
insert(fId, qst);
}
}
}
return true;
}
bool saveOne(int id, const QuestionFileT& q)
{
auto currentFile = make_file_name(id, testName);
const auto save_to_file = [&q](const QString& currentFile) -> bool
{
const auto save_l = [&q](QFile& f) -> bool
{
const auto save_once_l = [](const QString& data, QFile& f)
{
QByteArray utf8Data = data.toUtf8();
if (f.write(utf8Data) != utf8Data.size())
return false;
return f.write("\r\n") == 2;
};
if (!save_once_l(q.getQuestion(),f)) return false;
if (!save_once_l(q.getAnswer(),f)) return false;
for (const auto& it : q.getAnswerVariants())
if (!save_once_l(it, f)) return false;
return true;
};
QFile f(currentFile);
if (!f.open(QFile::WriteOnly|QFile::Text|QFile::Truncate)) {
return false;
}
return save_l(f);
};
bool success = save_to_file(currentFile);
if (success)
return true;
QFile::remove(currentFile);
return false;
}
public:
bool saveAll()
{
for (const auto& it : QuestionList)
if (!saveOne(it.first, it.second))
return false;
return true;
}
};
inline
QustionListT makeQuestionList(const QString& testName)
{
return testName.isEmpty() ? QustionListT() : QustionListT(testName);
}
//пользоввательский интерфейс
// this class view question and check answer
// if
class UserQuestionT : public QGroupBox
{
Q_OBJECT
std::vector<QRadioButton*> variants;
QPushButton* skip_button = new QPushButton(trUtf8("Пропустить"));
QPushButton* accept_button = new QPushButton(trUtf8("Ответить"));
public:
UserQuestionT() = delete;
UserQuestionT(const QuestionFileT& q, int q_counter, QWidget* parent = 0)
: QGroupBox(parent)
, question(q)
{
// отображение одного вопроса
this->setTitle(QString(trUtf8("Вопрос №%1").arg(q_counter)));
int index = 0;
auto l = new QLabel(q.getQuestion());
l->setWordWrap(true);
grid->addWidget(l, index++, 0, 1, 2);
for (const auto& it : q.getAnswerVariants()) {
auto wgt = new QRadioButton(it, this);
variants.push_back(wgt);
grid->addWidget(wgt, index++, 0, 1, 2);
}
variants[0]->setChecked(true);
grid->addWidget(skip_button, index, 0);
grid->addWidget(accept_button, index++, 1);
connect(skip_button, QPushButton::clicked, this, skip_slot);
connect(accept_button, QPushButton::clicked, this, accept_slot);
QHBoxLayout *layout_h0(new QHBoxLayout(this));
{
QVBoxLayout *main_layout_v(new QVBoxLayout);
{
//main_layout_v->addStretch(1);
main_layout_v->addLayout(grid);
//main_layout_v->addStretch(1);
}
//layout_h0->addStretch(1);
layout_h0->addLayout(main_layout_v);
//layout_h0->addStretch(1);
}
}
enum class AnswerErrorT {
ok = 1,
error,
skip
};
private:
QGridLayout* grid = new QGridLayout(0);
QuestionFileT question;
private slots:
void skip_slot()
{
emit complete(AnswerErrorT::skip);
}
void accept_slot()
{
for (QRadioButton* it : variants)
{
if (it->isChecked() && it->text() == question.getAnswer())
{
emit complete(AnswerErrorT::ok);
return;
}
}
emit complete(AnswerErrorT::error);
}
signals:
void complete(AnswerErrorT);
};
inline auto userResult(int trueAns, int totalQuest)
{
return abs(int(double(trueAns)*100/totalQuest));
}
class ShowResultT : public QWidget {
public:
ShowResultT(QWidget* parent, const TestResult &testResult)
: QWidget(parent)
{
// отображение результатов тестирования
QLabel* uName = new QLabel(QString(trUtf8("Тестируемый: %1").arg(testResult.user_name)));
QLabel* elapTime = new QLabel(QString(trUtf8("Время прохождения теста: %1").arg(string_time(testResult.elapsed_test_time))));
QLabel* total_l = new QLabel(QString(trUtf8("Вопросов всего: %1").arg(testResult.total_question)));
QLabel* correct = new QLabel(QString(trUtf8("Правильных ответов: %1").arg(testResult.true_answers)));
QLabel* unCorrect = new QLabel(QString(trUtf8("Неправильных ответов: %1").arg(testResult.false_answers)));
QLabel* skipQ = new QLabel(QString(trUtf8("Пропущено вопросов: %1").arg(testResult.skip_question)));
QLabel* result_l = new QLabel(QString(trUtf8("Ваш результат: %1%").arg(userResult(testResult.true_answers, testResult.total_question))));
grid->addWidget(uName, 0, 0);
grid->addWidget(total_l, 1, 0);
grid->addWidget(correct, 2, 0);
grid->addWidget(unCorrect, 3, 0);
grid->addWidget(skipQ, 4, 0);
grid->addWidget(result_l, 5, 0);
grid->addWidget(elapTime, 6, 0);
// изменение размеров окна
QHBoxLayout *layout_h0(new QHBoxLayout(this));
{
QVBoxLayout *main_layout_v(new QVBoxLayout);
{
main_layout_v->addStretch();
main_layout_v->addLayout(grid);
main_layout_v->addStretch();
}
layout_h0->addStretch();
layout_h0->addLayout(main_layout_v);
layout_h0->addStretch();
}
// auto stretch = new QVBoxLayout;
// stretch->addStretch();
// grid->addLayout(stretch, 4, 0);
}
private:
QGridLayout* grid = new QGridLayout(0);
};
// генерирует событие что время тестирования кончилось
class TimeCounter : public QObject
{
Q_OBJECT
void ticker()
{
if (timeLimit < elapsedTime) {
stop();
emit timeout();
}
else {
elapsedTime += 1;
emit timeLeft(timeLimit-elapsedTime);
}
}
public:
TimeCounter(uint timeLimit, QObject *parent=0)
: QObject(parent)
, timeLimit(timeLimit)
{
connect(timer, QTimer::timeout, this, ticker);
}
~TimeCounter()
{
delete timer;
}
void start()
{
elapsedTime = 0;
timer->start(1000);
}
auto getElapsedTime() const {
return elapsedTime;
}
void stop() {
timer->stop();
}
signals:
void timeout(); //время кончилось
void timeLeft(uint);// счетчик времени















