restrutured removing the go source

This commit is contained in:
jorenchik
2024-10-14 21:13:44 +03:00
parent ff4beecb5b
commit 2cef7007a2
138 changed files with 9911 additions and 1209 deletions

698
src/qtapp/trainWindow.cpp Normal file
View File

@@ -0,0 +1,698 @@
#include <QMainWindow>
#include <QWidget>
#include <QToolButton>
#include <algorithm>
#include <atomic>
#include <cstdint>
#include <format>
#include <cstdlib>
#include <exception>
#include <format>
#include <qabstractitemview.h>
#include <qboxlayout.h>
#include <qcoreevent.h>
#include <qlabel.h>
#include <qlayoutitem.h>
#include <qlistview.h>
#include <qnamespace.h>
#include <qscrollarea.h>
#include <qstandarditemmodel.h>
#include <qstringlistmodel.h>
#include <qtoolbutton.h>
#include <Qt>
#include <random>
#include <qwidget.h>
#include <QStandardItemModel>
#include <QDropEvent>
#include <QDrag>
#include <iostream>
#include <QStyledItemDelegate>
#include <QPainter>
#include "trainWindow.h"
#include "parser.h"
enum ElementState {
NEUTRAL = 0,
CORRECT,
INCORRECT
};
const auto listStyle =
"QListView {"
" background-color: white;"
" border: 0px solid black;"
"}"
"QListView::item::first {"
" padding: 5px 0 0 0;"
"}"
"QListView::item {"
" color: black;"
" padding: 5px;"
" background-color: none;"
"}"
"QListView::item:selected {"
" background-color: transparent;"
" padding-left: 0px;"
" margin-left: 0px;"
"}"
"QListView::item:hover {"
" background-color: lightyellow;"
" padding-left: 0px;"
" margin-left: 0px;"
"}";
class CustomItemDelegate : public QStyledItemDelegate {
public:
CustomItemDelegate(QObject *parent = nullptr) : QStyledItemDelegate(parent) {}
// Override the paint method to apply custom styling
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override {
painter->save();
Qt::GlobalColor color;
auto colorIdx = index.data(Qt::UserRole + 1).toInt() ;
switch (colorIdx) {
case NEUTRAL:
color = Qt::lightGray;
break;
case INCORRECT:
color = Qt::red;
break;
case CORRECT:
color = Qt::green;
break;
}
painter->setOpacity(0.5);
if (color) {
painter->fillRect(option.rect, color);
}
painter->restore();
QStyledItemDelegate::paint(painter, option, index);
}
};
class MultiChoiceListView : public QListView
{
Q_OBJECT
public:
explicit MultiChoiceListView(QWidget *parent = nullptr) : QListView(parent) {
setStyleSheet(listStyle);
setAcceptDrops(true);
setEditTriggers(QAbstractItemView::NoEditTriggers);
setFocusPolicy(Qt::NoFocus);
setSelectionMode(QAbstractItemView::NoSelection);
}
};
class OrderListView : public QListView
{
Q_OBJECT
public:
explicit OrderListView(QWidget *parent = nullptr) : QListView(parent) {
setStyleSheet(listStyle);
setAcceptDrops(true);
setFocusPolicy(Qt::NoFocus);
setEditTriggers(QAbstractItemView::NoEditTriggers);
setDragDropMode(QAbstractItemView::InternalMove);
}
protected:
void dropEvent(QDropEvent *event) override
{
QModelIndex sourceIndex = currentIndex();
QModelIndex targetIndex = indexAt(event->pos());
if (sourceIndex.isValid() && targetIndex.isValid()) {
QStandardItemModel *model = qobject_cast<QStandardItemModel *>(this->model());
if (model) {
QVariant sourceData = model->data(sourceIndex);
QVariant targetData = model->data(targetIndex);
model->setData(sourceIndex, targetData);
model->setData(targetIndex, sourceData);
event->ignore();
}
} else {
event->ignore();
}
}
};
class MoveListView : public QListView
{
Q_OBJECT
public:
explicit MoveListView(QWidget *parent = nullptr) : QListView(parent) {
setStyleSheet(listStyle);
setAcceptDrops(true);
setDragDropMode(QAbstractItemView::DragDrop);
setDefaultDropAction(Qt::MoveAction);
setEditTriggers(QAbstractItemView::NoEditTriggers);
setFocusPolicy(Qt::NoFocus);
}
protected:
void dragEnterEvent(QDragEnterEvent *event) override {
event->acceptProposedAction();
}
void dragMoveEvent(QDragMoveEvent *event) override {
event->acceptProposedAction();
}
void dropEvent(QDropEvent *event) override {
QListView *source = qobject_cast<QListView *>(event->source());
QStandardItemModel *sourceModel = qobject_cast<QStandardItemModel *>(source->model());
QStandardItemModel *targetModel = qobject_cast<QStandardItemModel *>(this->model());
if (source != this) {
QList<QStandardItem *> items = sourceModel->takeRow(source->currentIndex().row());
targetModel->appendRow(items);
}
event->ignore();
}
};
#include "trainWindow.moc"
struct GroupView {
QWidget widget;
QVBoxLayout vWidget;
QLabel label;
QScrollArea itemScroll;
QStandardItemModel itemModel;
MoveListView itemList;
};
// Main components
QMainWindow *trainWindow;
QWidget *trainWidget;
QVBoxLayout *vTrainWidget;
CustomItemDelegate *itemDelegate;
// Question
QWidget *questionBox;
QVBoxLayout *vQuestionBox;
QLabel *lQuestionText;
// Bottom buttons
QVBoxLayout *vButtonBox;
QWidget *actionButtons;
QHBoxLayout *hButtons;
QToolButton *btnPrev;
QSpacerItem *leftSpacer;
QToolButton *btnTriggerAnswer;
QSpacerItem *rightSpacer;
QToolButton *btnNext;
// Answer question
QLabel *answerText;
// Multi-choice question
QStandardItemModel *multiChoiceModel;
QListView *multiChoiceList;
// Order question
OrderListView *orderList;
QStandardItemModel *orderModel;
// Group question
QStandardItemModel *groupItemModel;
QListView *groupItemList;
QWidget *wGroupQuestion;
QVBoxLayout *vGroups;
std::vector<QStandardItemModel*> groupModels;
// Questions & State
std::vector<Question*> trainQuestions = std::vector<Question*>();
int32_t currentQuestionIndex = -1;
std::vector<GroupView*> groupViews;
std::default_random_engine rng;
std::vector<QStandardItem*> itemPool;
/*QStandardItem *makeItem(std::string content, bool isCheckable) {*/
/* auto *item = new QStandardItem(); */
/* item->setText(QString::fromStdString(content));*/
/* item->setCheckable(isCheckable);*/
/* return item;*/
/*};*/
#define ITEM_POOL_CHUNK 200
QStandardItem* acquireItem() {
if (itemPool.size() <= 0) {
for (int i = 0; i < ITEM_POOL_CHUNK; ++i) {
itemPool.push_back(new QStandardItem);
}
}
auto item = itemPool.back();
itemPool.pop_back();
return item;
}
void releaseItem(QStandardItem** item) {
itemPool.push_back(*item);
(*item) = nullptr;
}
void releaseAllItems() {
std::cout <<
std::format("Starting release, currently in the pool: {}", itemPool.size()) <<
std::endl;
auto releaseAllInModel = [](QStandardItemModel *model) {
auto itemCount = model->rowCount();
for (int i = 0; i < itemCount; ++i) {
auto item = model->item(0);
model->takeRow(item->row());
releaseItem(&item);
}
};
releaseAllInModel(multiChoiceModel);
releaseAllInModel(groupItemModel);
releaseAllInModel(orderModel);
for (auto groupView: groupViews) {
releaseAllInModel(&groupView->itemModel);
}
std::cout << std::format("All items released, currently in the pool: {}", itemPool.size()) << std::endl;
}
void hideQuestionElements() {
lQuestionText->hide();
answerText->hide();
orderList->hide();
multiChoiceList->hide();
wGroupQuestion->hide();
}
void setupAnswerQuestion(MultiElementQuestion *question) {
lQuestionText->setText(
QString::fromStdString(question->QuestionText)
);
lQuestionText->show();
auto ss = std::stringstream();
for (auto answerEl: question->Choices) {
ss << std::format("- {}", answerEl.Answer) << std::endl;
}
answerText->setText(
QString::fromStdString(ss.str())
);
if (answerText->isVisible()) {
answerText->hide();
}
QObject::connect(
btnTriggerAnswer,
&QToolButton::clicked,
[](bool checked) {
answerText->show();
btnTriggerAnswer->hide();
}
);
btnTriggerAnswer->show();
}
void setupOrderQuestion(MultiElementQuestion *question) {
lQuestionText->setText(
QString::fromStdString(question->QuestionText)
);
lQuestionText->show();
orderModel->clear();
auto shuffledAnswers = question->Choices;
std::shuffle(shuffledAnswers.begin(), shuffledAnswers.end(), rng);
for (auto answerEl: shuffledAnswers) {
auto *item = acquireItem();
item->setData(NEUTRAL, Qt::UserRole + 1);
item->setData(QVariant(), Qt::CheckStateRole);
item->setCheckable(false);
item->setText(QString::fromStdString(answerEl.Answer));
orderModel->appendRow(item);
}
orderList->show();
QObject::connect(
btnTriggerAnswer,
&QToolButton::clicked,
[question](bool checked) {
for (int i = 0; i < orderModel->rowCount(); ++i) {
auto item = orderModel->item(i, 0);
auto text = item->text();
auto isCorrect = question->Choices[i].Answer.compare(text.toStdString()) == 0;
if (isCorrect) {
item->setData(CORRECT, Qt::UserRole + 1);
} else {
item->setData(INCORRECT, Qt::UserRole + 1);
}
orderList->update();
}
btnTriggerAnswer->hide();
}
);
btnTriggerAnswer->show();
}
void setupMultiChoiceQuestion(MultiElementQuestion *question) {
lQuestionText->setText(
QString::fromStdString(question->QuestionText)
);
lQuestionText->show();
multiChoiceModel->clear();
for (auto answerEl: question->Choices) {
auto *item = acquireItem();
item->setText(QString::fromStdString(answerEl.Answer));
item->setData(NEUTRAL, Qt::UserRole + 1);
item->setCheckable(true);
item->setCheckState(Qt::CheckState::Unchecked);
multiChoiceModel->appendRow(item);
}
multiChoiceList->show();
QObject::connect(
btnTriggerAnswer,
&QToolButton::clicked,
[question](bool checked) {
for (int i = 0; i < multiChoiceModel->rowCount(); ++i) {
auto item = multiChoiceModel->item(i, 0);
auto isCorrect = question->Choices[i].IsCorrect == (item->checkState() == Qt::Checked);
if (isCorrect) {
item->setData(CORRECT, Qt::UserRole + 1);
} else {
item->setData(INCORRECT, Qt::UserRole + 1);
}
multiChoiceList->update();
}
btnTriggerAnswer->hide();
}
);
btnTriggerAnswer->show();
}
void setupGroupQuestion(GroupQuestion *question) {
auto groupSpacer = new QSpacerItem(
50, 50,
QSizePolicy::Minimum,
QSizePolicy::Expanding
);
lQuestionText->setText(
QString::fromStdString(question->QuestionText)
);
lQuestionText->show();
wGroupQuestion->show();
for (auto group: question->Groups) {
for (auto itemText: group.elements) {
auto *qItem = acquireItem();
qItem->setData(NEUTRAL, Qt::UserRole + 1);
qItem->setCheckable(false);
qItem->setData(QVariant(), Qt::CheckStateRole);
qItem->setText(QString::fromStdString(itemText));
groupItemModel->appendRow(qItem);
}
}
groupItemList->update();
auto makeGroup = []() {
auto groupView = new GroupView;
vGroups->addWidget(&groupView->label);
vGroups->addWidget(&groupView->itemList);
groupView->itemList.setModel(&groupView->itemModel);
groupView->itemList.setMaximumHeight(100);
groupView->itemList.setItemDelegate(itemDelegate);
/*groupView->itemScroll.setWidget(&groupView->itemScroll);*/
groupView->widget.setLayout(&groupView->vWidget);
groupView->widget.layout()->addWidget(&groupView->label);
groupView->widget.layout()->addWidget(&groupView->itemList);
return groupView;
};
for (int k = 0; k < groupViews.size(); k++) {
groupViews[k]->widget.hide();
}
for (int i = 0; i < question->Groups.size(); i++) {
GroupView *groupView;
if (i < groupViews.size()) {
groupView = groupViews[i];
} else {
groupView = makeGroup();
groupViews.push_back(groupView);
}
groupView->label.setText(
QString::fromStdString(question->Groups[i].name)
);
vGroups->addWidget(&groupView->widget);
groupView->widget.show();
}
vGroups->addItem(groupSpacer);
QObject::connect(
btnTriggerAnswer,
&QToolButton::clicked,
[question](bool checked) {
for (int i = 0; i < groupItemModel->rowCount(); ++i) {
auto item = groupItemModel->item(i, 0);
item->setData(INCORRECT, Qt::UserRole + 1);
}
for (int i = 0; i < groupViews.size(); i++) {
auto groupView = groupViews[i];
auto group = question->Groups[i];
for (int j = 0; j < groupView->itemModel.rowCount(); ++j) {
auto item = groupView->itemModel.item(j, 0);
auto itemText = item->text().toStdString();
bool found = false;
for (int k = 0; k < group.elements.size(); ++k) {
if (group.elements[k].compare(itemText) == 0) {
found = true;
break;
}
}
if (found) {
item->setData(CORRECT, Qt::UserRole + 1);
} else {
item->setData(INCORRECT, Qt::UserRole + 1);
}
}
groupView->itemList.update();
}
btnTriggerAnswer->hide();
}
);
btnTriggerAnswer->show();
}
void setupQuestion(Question *question) {
hideQuestionElements();
releaseAllItems();
QObject::disconnect(btnTriggerAnswer, 0, 0, 0);
if (auto *question = dynamic_cast<MultiElementQuestion*>(trainQuestions[currentQuestionIndex])) {
switch (question->type) {
case MultiElementType::Order:
setupOrderQuestion(question);
break;
case MultiElementType::MultiChoice:
setupMultiChoiceQuestion(question);
break;
case MultiElementType::Regular:
setupAnswerQuestion(question);
break;
}
} else if (auto *question = dynamic_cast<GroupQuestion*>(trainQuestions[currentQuestionIndex])) {
setupGroupQuestion(question);
}
}
void updatePaginationVisibility() {
if (currentQuestionIndex == 0) {
btnPrev->hide();
} else {
btnPrev->show();
}
if (currentQuestionIndex == trainQuestions.size() - 1) {
btnNext->hide();
} else {
btnNext->show();
}
}
void setQuestions(std::vector<Question*> questions) {
trainQuestions = questions;
if (questions.size() <= 0) {
return;
}
currentQuestionIndex = 0;
updatePaginationVisibility();
setupQuestion(trainQuestions[currentQuestionIndex]);
}
void initTrainWindow() {
unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
rng = std::default_random_engine(seed);
trainWindow = new QMainWindow();
trainWidget = new QWidget();
vTrainWidget = new QVBoxLayout();
trainWidget->setLayout(vTrainWidget);
trainWindow->setCentralWidget(trainWidget);
trainWidget->setLayout(vTrainWidget);
vTrainWidget->setAlignment(Qt::AlignCenter);
trainWidget->setObjectName("answer-question-widget");
itemDelegate = new CustomItemDelegate(multiChoiceList);
{ // Make the question box.
questionBox = new QWidget();
vQuestionBox = new QVBoxLayout();
questionBox->setLayout(vQuestionBox);
questionBox->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding);
vQuestionBox->setAlignment(Qt::AlignCenter);
}
{ // Make question text.
lQuestionText = new QLabel();
lQuestionText->setText("What is the capital of Latvia?");
lQuestionText->setWordWrap(true);
lQuestionText->setStyleSheet(QString(
"QLabel {"
"font-size: 20px;"
"}"
));
lQuestionText->setTextInteractionFlags(Qt::TextSelectableByMouse);
vQuestionBox->addWidget(lQuestionText);
lQuestionText->hide();
}
{ // Make multi-choice list.
multiChoiceModel = new QStandardItemModel();
multiChoiceList = new MultiChoiceListView();
multiChoiceList->setModel(multiChoiceModel);
multiChoiceList->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
questionBox->layout()->addWidget(multiChoiceList);
multiChoiceList->hide();
multiChoiceList->setItemDelegate(itemDelegate);
}
{ // Make answer text.
answerText = new QLabel();
answerText->setText("- Riga\n- Second line");
answerText->setWordWrap(true);
answerText->setStyleSheet(QString(
"QLabel {"
"font-size: 15px;"
"}"
));
answerText->setTextInteractionFlags(Qt::TextSelectableByMouse);
vQuestionBox->addWidget(answerText);
answerText->hide();
}
{ // Make order list.
orderModel = new QStandardItemModel();
orderList = new OrderListView();
orderList->setModel(orderModel);
orderList->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
orderList->setItemDelegate(itemDelegate);
// Connect to handle the drop event properly
QObject::connect(
orderModel,
&QStandardItemModel::rowsMoved,
[](const QModelIndex &, int sourceRow, int, const QModelIndex &, int destinationRow) {
if (sourceRow != destinationRow - 1) {
auto *movedItem = orderModel->takeItem(sourceRow);
orderModel->insertRow(destinationRow > sourceRow ? destinationRow - 1 : destinationRow, movedItem);
}
}
);
questionBox->layout()->addWidget(orderList);
orderList->hide();
}
{ // Make buttons menu. There are left middle and right buttons.
vButtonBox = new QVBoxLayout();
actionButtons = new QWidget();
hButtons = new QHBoxLayout();
btnPrev = new QToolButton();
leftSpacer = new QSpacerItem(50, 50, QSizePolicy::Expanding, QSizePolicy::Minimum);
btnTriggerAnswer = new QToolButton();
rightSpacer = new QSpacerItem(50, 50, QSizePolicy::Expanding, QSizePolicy::Minimum);
btnNext = new QToolButton();
hButtons->addWidget(btnPrev);
hButtons->addItem(leftSpacer);
hButtons->addWidget(btnTriggerAnswer);
hButtons->addItem(rightSpacer);
hButtons->addWidget(btnNext);
vButtonBox->addWidget(actionButtons);
actionButtons->setLayout(hButtons);
btnPrev->setText("Previous");
QObject::connect(btnPrev, &QToolButton::clicked, []() {
currentQuestionIndex--;
if (currentQuestionIndex > -1) {
setupQuestion(trainQuestions[currentQuestionIndex]);
}
updatePaginationVisibility();
});
btnTriggerAnswer->setText("Show answer");
btnNext->setText("Next");
QObject::connect(btnNext, &QToolButton::clicked, []() {
currentQuestionIndex++;
if (currentQuestionIndex < trainQuestions.size()) {
setupQuestion(trainQuestions[currentQuestionIndex]);
}
updatePaginationVisibility();
});
questionBox->setObjectName("question-box");
actionButtons->setStyleSheet(QString(
"QToolButton {"
"padding: 4px 5px;"
"font-size: 15px;"
"}"
));
}
{ // Make group question view.
wGroupQuestion = new QWidget();
wGroupQuestion->setStyleSheet(
"border:none;"
);
auto hGroupQuestion = new QHBoxLayout();
wGroupQuestion->setLayout(hGroupQuestion);
// Items on the left.
auto itemScroll = new QScrollArea();
auto vItems = new QVBoxLayout();
itemScroll->setLayout(vItems);
groupItemModel = new QStandardItemModel();
groupItemList = new MoveListView();
groupItemList->setItemDelegate(itemDelegate);
groupItemList->setModel(groupItemModel);
vItems->addWidget(groupItemList);
hGroupQuestion->addWidget(itemScroll);
// Items on the right.
auto groupScroll = new QScrollArea();
auto wGroupContainer = new QWidget();
vGroups = new QVBoxLayout();
groupScroll->setWidget(wGroupContainer);
groupScroll->setWidgetResizable(true);
wGroupContainer->setLayout(vGroups);
hGroupQuestion->addWidget(groupScroll);
wGroupQuestion->setStyleSheet("padding: 0; margin: 0;");
wGroupQuestion->hide();
questionBox->layout()->addWidget(wGroupQuestion);
}
vTrainWidget->addWidget(questionBox);
vTrainWidget->addWidget(actionButtons);
}