Skip to content
Snippets Groups Projects
Commit 42929696 authored by SeeLook's avatar SeeLook
Browse files

Implement QML version of exam summary. Manage exam/exercise continuation from there.

parent 48acd27f
Branches
Tags
No related merge requests found
......@@ -84,6 +84,8 @@ set(NOOTKA_SRC
main/texamhelp.cpp
main/ttiphandler.cpp
main/tmainscoreobject.cpp
main/texamsummary.cpp
main/tstartexamitem.cpp
dialogs/tdialogloaderobject.cpp
dialogs/tlevelcreatoritem.cpp
......
/***************************************************************************
* Copyright (C) 2017 by Tomasz Bojczuk *
* Copyright (C) 2017-2018 by Tomasz Bojczuk *
* seelook@gmail.com *
* *
* This program is free software; you can redistribute it and/or modify *
......@@ -23,6 +23,8 @@
#include "main/texamexecutor.h"
#include "main/texamview.h"
#include "main/ttiphandler.h"
#include "main/texamsummary.h"
#include "main/tstartexamitem.h"
#include "qtr.h"
#include <QtWidgets/qdialogbuttonbox.h>
......@@ -43,6 +45,8 @@ TdialogLoaderObject::TdialogLoaderObject(QObject* parent) :
qmlRegisterType<TexamExecutor>("Nootka.Exam", 1, 0, "Texecutor");
qmlRegisterType<TexamView>("Nootka.Exam", 1, 0, "Tresults");
qmlRegisterUncreatableType<TtipHandler>("Nootka.Exam", 1, 0, "TipHandler", QStringLiteral("You cannot create an instance of the TipHandler."));
qmlRegisterType<TexamSummary>("Nootka.Exam", 1, 0, "TexamSummary");
qmlRegisterType<TstartExamItem>("Nootka.Exam", 1, 0, "TstartExamItem");
qRegisterMetaType<Tlevel*>("Tlevel*");
m_firstTime = false;
......
/***************************************************************************
* Copyright (C) 2011-2016 by Tomasz Bojczuk *
* Copyright (C) 2011-2018 by Tomasz Bojczuk *
* seelook@gmail.com *
* *
* This program is free software; you can redistribute it and/or modify *
......@@ -18,74 +18,42 @@
#include "texamsummary.h"
#include <exam/texamview.h>
#include "texamexecutor.h"
#include "texamview.h"
#include "dialogs/tlevelpreviewitem.h"
#include <exam/texam.h>
#include <exam/tqaunit.h>
#include <exam/textrans.h>
#include <exam/tlevel.h>
#include <level/tlevelpreview.h>
#include <widgets/troundedlabel.h>
#include <tpath.h>
#include <plugins/tpluginsloader.h>
#include <QtWidgets/QtWidgets>
#if defined (Q_OS_ANDROID)
#include <touch/ttoucharea.h>
#include <Android/tandroid.h>
#endif
#include <qtr.h>
#include <QtCore/qtimer.h>
#include <QtCore/qdebug.h>
/** returns 2 columns row of table */
/**
* returns 2 columns row of table
*/
QString row2(const QString& S1, const QString& S2) {
return QString("<tr><td>%1: </td><td><b>%2</b></td></tr>").arg(S1).arg(S2);
}
bool showExamSummary(QWidget* mainWindow, Texam* exam, bool cont, bool* startExam) {
TexamSummary *ES = new TexamSummary(exam, cont, mainWindow);
TexamSummary::Eactions respond = ES->doExec();
if (startExam) {
if (respond == TexamSummary::e_startExam)
*startExam = true;
else
*startExam = false;
}
delete ES;
if (respond == TexamSummary::e_discard)
return false;
else
return true;
}
TexamSummary::TexamSummary(Texam* exam, bool cont, QWidget* parent) :
QDialog(parent),
m_exam(exam),
m_state(e_discard),
m_closeButt(0), m_examButton(0),
m_mainWIndow(parent)
TexamSummary::TexamSummary(QQuickItem* parent) :
QQuickItem(parent)
{
#if defined (Q_OS_ANDROID)
showMaximized();
#else
setWindowTitle(tr("Exam results"));
setWindowIcon(QIcon(Tpath::img("startExam")));
#endif
QHBoxLayout *lay = new QHBoxLayout();
//------- left layout -----------------------
m_leftLay = new QVBoxLayout();
QString font20 = "<b><big>";
QLabel *userNameLab = new QLabel(tr("student:") + QString(" %2<u>%1</u></b>").arg(exam->userName()).arg(font20), this);
m_leftLay->addWidget(userNameLab, 0, Qt::AlignCenter);
m_exam = EXECUTOR->exam();
/*
TroundedLabel *questNrLab = new TroundedLabel("<center>" + tr("Number of questions:") + QString("%2 %1</big></b>").arg(exam->count()).arg(font20) +
QString("<br>%1: %2%3</big></b>").arg(TexTrans::corrAnswersNrTxt()).arg(font20).
arg(exam->count() - exam->mistakes() - exam->halfMistaken()) +
QString("<br>%1: %2%3</big></b>").arg(TexTrans::mistakesNrTxt()).arg(font20).arg(exam->mistakes()) +
QString("<br>%1: %2%3</big></b>").arg(TexTrans::halfMistakenTxt()).arg(font20).arg(exam->halfMistaken())
,this);
m_leftLay->addWidget(questNrLab);
QVBoxLayout *timeLay = new QVBoxLayout();
QGroupBox *timeGr = new QGroupBox(tr("times:"), this);
TroundedLabel *timeLab = new TroundedLabel("<table>" +
row2(TexTrans::totalTimetxt(), TexamView::formatedTotalTime(exam->totalTime() * 1000)) +
......@@ -93,79 +61,26 @@ TexamSummary::TexamSummary(Texam* exam, bool cont, QWidget* parent) :
row2(TexTrans::averAnsverTimeTxt(), QString("%1 s").
arg((qreal)exam->averageReactonTime()/10.0, 0, 'f', 1, '0')) +
"</table>", this);
timeLab->setContentsMargins(5, 5, 5, 5);
timeLay->addWidget(timeLab);
timeGr->setLayout(timeLay);
m_leftLay->addWidget(timeGr);
QPushButton *analyseButt = new QPushButton(tr("Analyze"), this);
analyseButt->setIcon(QIcon(Tpath::img("charts")));
analyseButt->setIconSize(QSize(48, 48));
if (exam->count() == 0)
analyseButt->setDisabled(true);
#if defined (Q_OS_ANDROID) // TODO: delete if mobile version will support analysis
analyseButt->hide();
m_sendButt = new QPushButton(qTR("QShortcut", "Send"), this);
m_sendButt->setIcon(QIcon(Tpath::img("nootka-exam")));
m_sendButt->setIconSize(QSize(48, 48));
#endif
m_okButt = new QPushButton(qTR("QPlatformTheme", "Close"), this);
if (cont) {
m_okButt->setText(qTR("QWizard", "Continue"));
m_okButt->setIcon(QIcon(Tpath::img("exam")));
m_closeButt = new QPushButton(qTR("QPlatformTheme", "Close"), this);
m_closeButt->setIcon(QIcon(Tpath::img("exit")));
m_closeButt->setIconSize(QSize(48, 48));
connect(m_closeButt, &QPushButton::clicked, this, &TexamSummary::closeSlot);
} else
m_okButt->setIcon(QIcon(Tpath::img("exit")));
m_okButt->setIconSize(QSize(48, 48));
auto buttLay = new QHBoxLayout;
buttLay->addWidget(m_okButt);
buttLay->addWidget(analyseButt);
m_leftLay->addStretch(1);
m_leftLay->addLayout(buttLay);
#if defined (Q_OS_ANDROID)
m_leftLay->addWidget(m_sendButt);
#endif
if (cont)
m_leftLay->addWidget(m_closeButt);
lay->addLayout(m_leftLay);
//------- right layout -----------------------
QVBoxLayout *rightLay = new QVBoxLayout();
TlevelPreview *levelWdg = new TlevelPreview(this);
rightLay->addWidget(levelWdg);
levelWdg->setLevel(*(exam->level()));
levelWdg->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
QVBoxLayout *resLay = new QVBoxLayout();
QGroupBox *resGr = new QGroupBox(tr("Results:"), this);
*/
QString effStr;
if (exam->mistakes() || exam->halfMistaken()) {
// effStr = row2(TexamView::mistakesNrTxt(), QString::number(exam->mistakes()));
// effStr += row2(TexamView::corrAnswersNrTxt(), QString::number(exam->count()-exam->mistakes()));
if (m_exam->mistakes() || m_exam->halfMistaken()) {
float wAccid = 0.0, wKey = 0.0, wNote = 0.0, wOctave = 0.0, wStyle = 0.0, wPos = 0.0, wString = 0.0, wTotal;
float wInto = 0.0, wLittle = 0.0, wPoor = 0.0;
for(int i=0; i<exam->count(); i++) {
if (!exam->question(i)->isCorrect()) {
if(exam->question(i)->wrongAccid()) wAccid++;
if(exam->question(i)->wrongKey()) wKey++;
if(exam->question(i)->wrongNote()) wNote++;
if(exam->question(i)->wrongOctave()) wOctave++;
if(exam->question(i)->wrongStyle()) wStyle++;
if(exam->question(i)->wrongPos()) wPos++;
if(exam->question(i)->wrongString()) wString++;
if(exam->question(i)->wrongIntonation()) wInto++;
if(exam->question(i)->littleNotes()) wLittle++;
if(exam->question(i)->poorEffect()) wPoor++;
for(int i = 0; i < m_exam->count(); ++i) {
if (!m_exam->question(i)->isCorrect()) {
if(m_exam->question(i)->wrongAccid()) wAccid++;
if(m_exam->question(i)->wrongKey()) wKey++;
if(m_exam->question(i)->wrongNote()) wNote++;
if(m_exam->question(i)->wrongOctave()) wOctave++;
if(m_exam->question(i)->wrongStyle()) wStyle++;
if(m_exam->question(i)->wrongPos()) wPos++;
if(m_exam->question(i)->wrongString()) wString++;
if(m_exam->question(i)->wrongIntonation()) wInto++;
if(m_exam->question(i)->littleNotes()) wLittle++;
if(m_exam->question(i)->poorEffect()) wPoor++;
}
}
effStr += "<tr><td colspan=\"2\">-------- " + tr("Kinds of mistakes") + QStringLiteral(": --------</td></tr>");
effStr += "<tr><td colspan=\"2\">-------- " + tr("Kinds of mistakes") + QLatin1String(": --------</td></tr>");
wTotal = wAccid + wKey + wNote + wOctave + wStyle + wPos + wString + wInto + wLittle + wPoor;
QString cp = QStringLiteral("%)"); // closing percent '%)'
if (wNote)
......@@ -191,104 +106,76 @@ TexamSummary::TexamSummary(Texam* exam, bool cont, QWidget* parent) :
effStr += row2(QApplication::translate("AnswerText", "poor effectiveness"),
QString("%1 (").arg(wPoor) + QString::number(qRound(wPoor * 100.0 / wTotal)) + cp);
}
TroundedLabel *resLab = new TroundedLabel(QStringLiteral("<table>") +
row2(TexTrans::effectTxt(), QString::number(qRound(exam->effectiveness())) + QStringLiteral("%")) + effStr + QStringLiteral("</table>"), this);
resLab->setContentsMargins(5, 5, 5, 5);
resLay->addWidget(resLab);
resGr->setLayout(resLay);
rightLay->addWidget(resGr);
lay->addLayout(rightLay);
#if defined (Q_OS_ANDROID)
auto ta = new TtouchArea(this);
ta->setLayout(lay);
auto touchLay = new QVBoxLayout;
touchLay->addWidget(ta);
setLayout(touchLay);
#else
setLayout(lay);
#endif
m_results = tr("Results:") + QLatin1String("<table>") +
row2(TexTrans::effectTxt(), QString::number(qRound(m_exam->effectiveness())) + QLatin1String("%")) + effStr + QLatin1String("</table>");
}
connect(analyseButt, &QPushButton::clicked, this, &TexamSummary::analyseSlot);
connect(m_okButt, &QPushButton::clicked, this, &TexamSummary::continueSlot);
#if defined (Q_OS_ANDROID)
connect(m_sendButt, &QPushButton::clicked, this, &TexamSummary::sendExamSlot);
#endif
QString TexamSummary::title() const {
if (m_exam) {
if (m_exam->isExercise())
setForExercise();
return tr("Progress of exercises");
else
return tr("Exam results");
}
return QString();
}
TexamSummary::Eactions TexamSummary::doExec() {
QDialog::exec();
return m_state;
void TexamSummary::setLevelPreview(TlevelPreviewItem* lp) {
m_levelPreview = lp;
m_levelPreview->setLevel(m_exam->level());
}
void TexamSummary::setForExercise() {
setWindowTitle(tr("Progress of exercises"));
m_examButton = new QPushButton(tr("Pass an exam"), this);
m_examButton->setToolTip(tr("Finish exercise and pass an exam on this level."));
m_examButton->setIcon(QIcon(Tpath::img("exam")));
m_examButton->setIconSize(QSize(48, 48));
connect(m_examButton, &QPushButton::clicked, this, &TexamSummary::startExamSlot);
if (m_closeButt) {
m_okButt->setIcon(QIcon(Tpath::img("practice")));
m_closeButt->setText(tr("Finish this exercise"));
m_leftLay->insertWidget(m_leftLay->count() - 1, m_examButton);
} else
m_leftLay->addWidget(m_examButton);
#if defined (Q_OS_ANDROID)
m_sendButt->hide(); // don't send exercise file
#endif
QString TexamSummary::student() const {
return tr("student:") + QString("<font size=\"5\"><b> %1</b></font>").arg(m_exam->userName());
}
//#################################################################
// SLOTS
//#################################################################
void TexamSummary::analyseSlot() {
TpluginsLoader loader;
if (loader.load(TpluginsLoader::e_analyzer)) {
loader.init(QString(), m_mainWIndow, m_exam);
}
bool TexamSummary::isExercise() const {
return m_exam && m_exam->isExercise();
}
#if defined (Q_OS_ANDROID)
void TexamSummary::sendExamSlot() {
QString space = QStringLiteral(" ");
QString br = QStringLiteral("\n");
QString message = qTR("TexamSummary", "student:") + space + m_exam->userName() + br;
message += qTR("TanalysDialog", "level:") + space + m_exam->level()->name + br;
message += qTR("TexamSummary", "Number of questions:") + space + QString::number(m_exam->count()) + br;
Tandroid::sendExam(tr("Send exam file"), message, m_exam->fileName());
bool TexamSummary::continueExecutor() {
m_accepted = true;
if (m_exam->isExercise()) {
EXECUTOR->continueExercise();
return true;
} else
return EXECUTOR->continueInit();
}
#endif
void TexamSummary::closeSlot() {
m_state = e_discard;
close();
}
void TexamSummary::continueSlot() {
m_state = e_continue;
close();
void TexamSummary::exerciseToExam() {
m_accepted = true;
EXECUTOR->restoreExerciseAfterSummary();
EXECUTOR->exerciseToExam();
}
void TexamSummary::startExamSlot() {
m_state = e_startExam;
close();
void TexamSummary::closeSummary() {
if (!m_accepted) {
if (EXECUTOR->summaryReason() == TexamExecutor::SumFinishExer)
QTimer::singleShot(10, [=]{ EXECUTOR->finishExerciseAfterSummary(); });
else
QTimer::singleShot(10, [=]{ EXECUTOR->closeExecutor(); });
}
}
bool TexamSummary::discardedAtBegin() const {
return EXECUTOR->summaryReason() == TexamExecutor::SumContExam && !EXECUTOR->isInitialized();
}
bool TexamSummary::enableContinue() const {
return m_exam->isExercise() || !EXECUTOR->isInitialized();
}
int TexamSummary::buttColumsCount() const {
return (!enableContinue() && isExercise()) || (enableContinue() && !isExercise()) ? 3 : 2;
}
/***************************************************************************
* Copyright (C) 2011-2016 by Tomasz Bojczuk *
* Copyright (C) 2011-2018 by Tomasz Bojczuk *
* seelook@gmail.com *
* *
* This program is free software; you can redistribute it and/or modify *
......@@ -19,55 +19,80 @@
#ifndef TEXAMSUMMARY_H
#define TEXAMSUMMARY_H
#include <QtWidgets/qdialog.h>
class QVBoxLayout;
class QPushButton;
#include <QtQuick/qquickitem.h>
class Texam;
class TlevelPreviewItem;
/** Global function to create and display an exam summary dialog.
* If it returns false - user don't want to continue an exam
* @p startExam is a pointer to know does user want to start exam on exercise level.
* @class QMainWindow has to be put for proper size of analyze dialog. */
bool showExamSummary(QWidget* mainWindow, Texam* exam, bool cont, bool* startExam = 0);
/**
* Dialog window with exam summary
* Content of a dialog with exam summary
* When @param cont is true On button is shown text 'continue'
*/
class TexamSummary : public QDialog
class TexamSummary : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(TlevelPreviewItem* levelPreview READ levelPreview WRITE setLevelPreview)
// Exam properties
Q_PROPERTY(QString student READ student NOTIFY updateExam)
Q_PROPERTY(QString results READ results NOTIFY updateExam)
public:
TexamSummary(Texam *exam, bool cont = false, QWidget *parent = 0);
explicit TexamSummary(QQuickItem* parent = nullptr);
TlevelPreviewItem* levelPreview() { return m_levelPreview; }
void setLevelPreview(TlevelPreviewItem* lp);
QString student() const;
QString results() const { return m_results; }
Q_INVOKABLE QString title() const;
Q_INVOKABLE bool isExercise() const;
Q_INVOKABLE bool continueExecutor();
Q_INVOKABLE void exerciseToExam();
Q_INVOKABLE void closeSummary();
enum Eactions { e_continue, e_discard, e_startExam };
Eactions doExec();
/**
* Indicates whether the summary dialog was discarded when exam is presented before start.
* Summary dialog has to delete executor then.
* In other cases (when exam or exercise was performed and executor was fully initialized)
* the executor destroys itself
*/
Q_INVOKABLE bool discardedAtBegin() const;
/**
* When 'continue' button can be displayed to user
*/
Q_INVOKABLE bool enableContinue() const;
/**
* Number of columns to contain navigation buttons. (2 or 3)
* To minimize QML parsing
*/
Q_INVOKABLE int buttColumsCount() const;
/** By default it was created for exams. This method adjust the dialog for exercise context:
* - changes window title,
* - continue button icon
* - adds button to start exam on current exercise level */
void setForExercise();
signals:
void updateExam();
protected:
void closeSlot();
void analyseSlot();
void continueSlot();
void startExamSlot();
#if defined (Q_OS_ANDROID)
void sendExamSlot();
#endif
// void closeSlot();
// void analyseSlot();
// void continueSlot();
// void startExamSlot();
// #if defined (Q_OS_ANDROID)
// void sendExamSlot();
// #endif
private:
Texam *m_exam;
Eactions m_state;
QPushButton *m_closeButt, *m_okButt, *m_examButton;
#if defined (Q_OS_ANDROID)
QPushButton *m_sendButt;
#endif
QVBoxLayout *m_leftLay;
QWidget *m_mainWIndow;
Texam *m_exam = nullptr;
TlevelPreviewItem *m_levelPreview = nullptr;
bool m_accepted = false;
QString m_results;
};
#endif // TEXAMSUMMARY_H
......@@ -107,6 +107,7 @@
<file alias="exam/ExamTip.qml">qml/exam/ExamTip.qml</file>
<file alias="exam/QuestionTip.qml">qml/exam/QuestionTip.qml</file>
<file alias="exam/ResultTip.qml">qml/exam/ResultTip.qml</file>
<file alias="exam/ExamSummary.qml">qml/exam/ExamSummary.qml</file>
<file alias="qtquickcontrols2.conf">qml/qtquickcontrols2.conf</file>
<!-- <file alias="+android/qtquickcontrols2.conf">qml/+android/qtquickcontrols2.conf</file> -->
......
/** This file is part of Nootka (http://nootka.sf.net) *
* Copyright (C) 2017 by Tomasz Bojczuk (seelook@gmail.com) *
* Copyright (C) 2017-2018 by Tomasz Bojczuk (seelook@gmail.com) *
* on the terms of GNU GPLv3 license (http://www.gnu.org/licenses) */
import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Window 2.2
import QtQuick.Dialogs 1.2 as Old
import QtQuick.Dialogs 1.3 as Old
import Nootka 1.0
import Nootka.Dialogs 1.0
......@@ -86,6 +86,10 @@ Old.Dialog {
var c = Qt.createComponent("qrc:/exam/StartExam.qml")
currentDialog = c.createObject(container)
break
case Nootka.ExamSummary:
var c = Qt.createComponent("qrc:/exam/ExamSummary.qml")
currentDialog = c.createObject(container)
break
}
SOUND.stopListen()
open()
......
/** This file is part of Nootka (http://nootka.sf.net) *
* Copyright (C) 2017 by Tomasz Bojczuk (seelook@gmail.com) *
* Copyright (C) 2017-2018 by Tomasz Bojczuk (seelook@gmail.com) *
* on the terms of GNU GPLv3 license (http://www.gnu.org/licenses) */
import QtQuick 2.9
......@@ -17,6 +17,7 @@ Texecutor {
onTitleChanged: nootkaWindow.title = title
onExamActionsChanged: nootkaWindow.mainMenu.toolBar.examActions = examActions
onExamSummary: nootkaWindow.showDialog(Nootka.ExamSummary)
Connections {
target: tipHandler
......
/** This file is part of Nootka (http://nootka.sf.net) *
* Copyright (C) 2018 by Tomasz Bojczuk (seelook@gmail.com) *
* on the terms of GNU GPLv3 license (http://www.gnu.org/licenses) */
import QtQuick 2.9
import QtQuick.Controls 2.2
import Nootka.Exam 1.0
import "../level"
import "../"
TexamSummary {
id: summDialog
width: parent.width; height: parent.height
levelPreview: previewItem
Column {
Grid {
columns: 2
padding: summDialog.width / 200
Tflickable {
height: summDialog.height - buttGrid.height - summDialog.width / 50; width: summDialog.width * 0.49
contentHeight: summCol.height
Column {
id: summCol
spacing: summDialog.width / 200
width: parent.width
Text {
anchors.horizontalCenter: parent.horizontalCenter
text: student; textFormat: Text.StyledText
}
Text {
anchors.horizontalCenter: parent.horizontalCenter
width: parent.width * 0.9
text: results; textFormat: Text.RichText
}
}
}
LevelPreview {
id: previewItem
width: summDialog.width * 0.49; height: summDialog.height - buttGrid.height - summDialog.width / 50
}
}
Grid {
id: buttGrid
anchors.horizontalCenter: parent.horizontalCenter
spacing: summDialog.width / 100
columns: buttColumsCount()
TiconButton {
visible: enableContinue()
width: summDialog.width / 4
pixmap: Noo.pix(isExercise() ? "practice" : "exam"); iconHeight: summDialog.height / 15
text: Noo.TR("QWizard", "Continue")
onClicked: { continueExecutor(); dialLoader.close() }
}
TiconButton {
enabled: false
width: summDialog.width / 4
pixmap: Noo.pix("charts"); iconHeight: summDialog.height / 15
text: qsTr("Analyze")
// onClicked:
}
TiconButton {
visible: isExercise()
width: summDialog.width / 4
pixmap: Noo.pix("exam"); iconHeight: summDialog.height / 15
text: qsTr("Pass an exam")
onClicked: { exerciseToExam(); dialLoader.close() }
}
TiconButton {
width: summDialog.width / 4
pixmap: Noo.pix("exit"); iconHeight: summDialog.height / 15
text: Noo.TR("QPlatformTheme", "Close")
onClicked: dialLoader.close()
}
}
}
Component.onCompleted: {
if (Noo.isAndroid()) {
dialLoader.buttons = []
} else {
dialLoader.standardButtons = 0
dialLoader.title = summDialog.title()
}
}
Component.onDestruction: {
if (discardedAtBegin()) {
nootkaWindow.executor.destroy()
GLOB.isExam = false
} else
closeSummary()
}
}
......@@ -26,7 +26,7 @@ Column {
enabled: creator.notSaved
width: levelsPage.width / 4
pixmap: Noo.pix("notSaved"); iconHeight: levelsPage.height / 15
text: qsTr("Save")
text: Noo.TR("QShortcut", "Save")
onClicked: creator.saveLevel()
}
TiconButton {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment