Initial commit
This commit is contained in:
@@ -0,0 +1,564 @@
|
||||
#include "demonstrationWidget.h"
|
||||
|
||||
inline void DemonstrationWidget::TableInformation::clear() {
|
||||
for (size_t *value :
|
||||
{&m_maxCollision, &m_numberOfSuccesses, &m_numberOfFailures}) {
|
||||
*value = 0;
|
||||
}
|
||||
for (double *value : {&m_lastCoefficient, &m_maxCoefficient}) {
|
||||
*value = 0;
|
||||
}
|
||||
for (auto *series : {m_pSeriesNumberOfCollisions}) {
|
||||
series->removePoints(0, series->count());
|
||||
}
|
||||
m_pSeriesNumberOfCollisions->append(0, 0);
|
||||
|
||||
m_lastCoord = {0, 0};
|
||||
m_lastValue = {'\0', '\0', '\0', '\0', '\0'};
|
||||
m_lastInsertion = false;
|
||||
}
|
||||
|
||||
DemonstrationWidget::DemonstrationWidget(QWidget *pWidget)
|
||||
: QWidget(pWidget),
|
||||
m_pChartCollisionsView(new QChartView(new QChart, this)),
|
||||
m_pStandardModel(nullptr), m_stop(false), m_pFunction(nullptr),
|
||||
m_complicatedFunction(nullptr), m_pAlgorithm(nullptr),
|
||||
m_alphabetPower(endChar - firstChar + 1), m_pTable(nullptr),
|
||||
m_tableInformation(new QLineSeries(m_pChartCollisionsView->chart())) {
|
||||
setupUi(dynamic_cast<QWidget *>(this));
|
||||
|
||||
m_tableView->horizontalHeader()->setSectionResizeMode(
|
||||
QHeaderView::ResizeToContents);
|
||||
QGridLayout *gridLayout = new QGridLayout;
|
||||
|
||||
/*
|
||||
* Сhart
|
||||
*/
|
||||
|
||||
auto *axisX = new QValueAxis();
|
||||
auto *axisY = new QValueAxis();
|
||||
axisX->setTickCount(20);
|
||||
axisY->setTickCount(10);
|
||||
axisX->setRange(1, 2);
|
||||
axisY->setRange(0, 1);
|
||||
|
||||
QChart *chartCollision = m_pChartCollisionsView->chart();
|
||||
chartCollision->setTitle("Graph of collision changes");
|
||||
chartCollision->legend()->hide();
|
||||
chartCollision->addAxis(axisX, Qt::AlignBottom);
|
||||
chartCollision->addAxis(axisY, Qt::AlignLeft);
|
||||
chartCollision->addSeries(m_tableInformation.m_pSeriesNumberOfCollisions);
|
||||
m_tableInformation.m_pSeriesNumberOfCollisions->attachAxis(axisX);
|
||||
m_tableInformation.m_pSeriesNumberOfCollisions->attachAxis(axisY);
|
||||
|
||||
/*
|
||||
* View(-s)
|
||||
*/
|
||||
|
||||
m_pChartCollisionsView->installEventFilter(this);
|
||||
m_pChartCollisionsView->setRubberBand(QChartView::RectangleRubberBand);
|
||||
m_pChartCollisionsView->setRenderHint(QPainter::Antialiasing);
|
||||
m_pChartCollisionsView->setMinimumHeight(m_tableView->minimumHeight());
|
||||
m_pChartCollisionsView->setMinimumWidth(m_tableView->minimumWidth());
|
||||
|
||||
/*
|
||||
* Splitter
|
||||
*/
|
||||
|
||||
QSplitter *splitter = new QSplitter(Qt::Vertical);
|
||||
splitter->addWidget(m_tableView);
|
||||
splitter->addWidget(m_pChartCollisionsView);
|
||||
|
||||
gridLayout->addWidget(splitter, 0, 0, 1, 1);
|
||||
gridLayout->addWidget(m_rightScrollArea, 0, 1, 1, 1);
|
||||
gridLayout->addWidget(m_frmProgressBar, 1, 0, 1, 2);
|
||||
setLayout(gridLayout);
|
||||
|
||||
/*
|
||||
* Validator and HTML theory
|
||||
*/
|
||||
|
||||
m_leFrom->setValidator(new LatinValidator(m_leFrom));
|
||||
m_leInsert->setValidator(new LatinValidator(m_leInsert));
|
||||
|
||||
/*
|
||||
* Signal & slots
|
||||
*/
|
||||
|
||||
connect(m_pbReset, &QPushButton::clicked, this,
|
||||
&DemonstrationWidget::slotClickedReset);
|
||||
connect(m_pbApply, &QPushButton::clicked, this,
|
||||
&DemonstrationWidget::slotClickedApply);
|
||||
connect(m_pbStatistics, &QPushButton::clicked, this,
|
||||
&DemonstrationWidget::slotClickedStatistics);
|
||||
connect(m_pbMakeItems, &QPushButton::clicked, this,
|
||||
&DemonstrationWidget::slotClickedMakeItems);
|
||||
connect(m_pbInsert, &QPushButton::clicked, this,
|
||||
&DemonstrationWidget::slotClickedInsert);
|
||||
connect(m_pbFindFree, &QPushButton::clicked, this,
|
||||
&DemonstrationWidget::slotClickedFindFree);
|
||||
connect(m_pbStop, &QPushButton::clicked, this,
|
||||
&DemonstrationWidget::slotClickedStop);
|
||||
connect(m_pbFocus, &QPushButton::clicked, this,
|
||||
&DemonstrationWidget::slotClickedFocus);
|
||||
|
||||
connect(m_rbFNV1a32bit, &QRadioButton::toggled, this,
|
||||
&DemonstrationWidget::slotUpdateAdditionalInputData);
|
||||
connect(m_rbPseudorandomProbing, &QRadioButton::toggled, this,
|
||||
&DemonstrationWidget::slotUpdateAdditionalInputData);
|
||||
connect(m_rbDoubleHashing, &QRadioButton::toggled, this,
|
||||
&DemonstrationWidget::slotUpdateAdditionalInputData);
|
||||
connect(m_rbChains, &QRadioButton::toggled, this,
|
||||
&DemonstrationWidget::slotUpdateAdditionalInputData);
|
||||
|
||||
/*
|
||||
* GUI
|
||||
*/
|
||||
m_frmProgressBar->hide();
|
||||
m_frmFirstInputData->hide();
|
||||
m_frmSecondInputData->hide();
|
||||
m_gbInputData->hide();
|
||||
}
|
||||
|
||||
/*
|
||||
* Inserting elements
|
||||
*/
|
||||
|
||||
void DemonstrationWidget::successfulInsertion(const table_type *,
|
||||
const value_type &value,
|
||||
std::pair<size_t, size_t> index,
|
||||
size_t numberOfCollisions) {
|
||||
m_tableInformation.m_lastCoord = index;
|
||||
m_tableInformation.m_lastValue = value;
|
||||
m_tableInformation.m_lastInsertion = true;
|
||||
++m_tableInformation.m_numberOfSuccesses;
|
||||
|
||||
m_tableInformation.m_lastCollision = numberOfCollisions;
|
||||
m_tableInformation.m_maxCollision = {numberOfCollisions >
|
||||
m_tableInformation.m_maxCollision
|
||||
? numberOfCollisions
|
||||
: m_tableInformation.m_maxCollision};
|
||||
|
||||
m_tableInformation.m_lastCollision = numberOfCollisions;
|
||||
if (numberOfCollisions > m_tableInformation.m_maxCollision) {
|
||||
m_tableInformation.m_maxCollision = numberOfCollisions;
|
||||
}
|
||||
|
||||
m_tableInformation.m_pSeriesNumberOfCollisions->append(
|
||||
m_tableInformation.m_numberOfSuccesses, numberOfCollisions);
|
||||
|
||||
QModelIndex modelIndex = m_pStandardModel->index(index.first, index.second);
|
||||
if (getCurrentTableType() == TableType::Chain) {
|
||||
updateColumns(index.first);
|
||||
} else {
|
||||
m_pStandardModel->setData(modelIndex, QVariant(toQString(value)));
|
||||
}
|
||||
}
|
||||
|
||||
void DemonstrationWidget::unsuccessfulInsertion(const table_type *,
|
||||
const value_type &value) {
|
||||
m_tableInformation.m_lastValue = value;
|
||||
m_tableInformation.m_lastInsertion = false;
|
||||
++m_tableInformation.m_numberOfFailures;
|
||||
}
|
||||
|
||||
inline void DemonstrationWidget::updateColumns(size_t rowIndex) {
|
||||
using namespace BusinessLogic::Hash::Table;
|
||||
|
||||
auto tableRow =
|
||||
dynamic_cast<BusinessLogic::Hash::Table::Chains *>(m_pTable.get())
|
||||
->row(rowIndex);
|
||||
size_t modelColumnCount = m_pStandardModel->columnCount();
|
||||
size_t tableColumnCount = tableRow.size();
|
||||
|
||||
if (modelColumnCount < tableColumnCount) {
|
||||
m_pStandardModel->insertColumns(
|
||||
modelColumnCount,
|
||||
std::max(tableColumnCount - modelColumnCount,
|
||||
static_cast<size_t>(std::ceil(
|
||||
std::sqrt(m_tableInformation.m_lastCoefficient)))));
|
||||
}
|
||||
|
||||
QModelIndex modelIndex;
|
||||
auto iterator = tableRow.begin();
|
||||
for (size_t i = 0, size = tableRow.size(); i < size; ++i, ++iterator) {
|
||||
modelIndex = m_pStandardModel->index(rowIndex, i);
|
||||
m_pStandardModel->setData(modelIndex, QVariant(toQString(*iterator)));
|
||||
}
|
||||
}
|
||||
|
||||
inline void DemonstrationWidget::updateCharts() {
|
||||
m_pChartCollisionsView->chart()
|
||||
->axes(Qt::Horizontal)
|
||||
.front()
|
||||
->setRange(1, m_tableInformation.m_numberOfSuccesses);
|
||||
m_pChartCollisionsView->chart()
|
||||
->axes(Qt::Vertical)
|
||||
.front()
|
||||
->setRange(0, m_tableInformation.m_maxCollision);
|
||||
m_pChartCollisionsView->chart()->resetMatrix();
|
||||
}
|
||||
|
||||
inline void DemonstrationWidget::updateTableView() {
|
||||
m_tableView->scrollTo(
|
||||
m_pStandardModel->index(m_tableInformation.m_lastCoord.first,
|
||||
m_tableInformation.m_lastCoord.second),
|
||||
QAbstractItemView::PositionAtCenter);
|
||||
m_tableView->repaint();
|
||||
}
|
||||
|
||||
inline DemonstrationWidget::value_type &
|
||||
DemonstrationWidget::next(value_type &value) const {
|
||||
auto rbegin = value.rbegin();
|
||||
auto rend = value.rend();
|
||||
|
||||
for (; rbegin != rend; ++rbegin) {
|
||||
if (*rbegin >= firstChar && *rbegin < endChar) {
|
||||
++(*rbegin);
|
||||
return value;
|
||||
} else if (*rbegin == '\0') {
|
||||
*rbegin = firstChar;
|
||||
return value;
|
||||
} else {
|
||||
*rbegin = firstChar;
|
||||
}
|
||||
}
|
||||
value = toStdArray(std::string(4, '\0') + firstChar);
|
||||
return value;
|
||||
}
|
||||
|
||||
inline bool DemonstrationWidget::checkBeforeInsertion(bool tableTypeIsChain) {
|
||||
size_t quantityOfInserts = m_sbQuantity->value();
|
||||
if (tableTypeIsChain &&
|
||||
(m_pTable->numberOfBuckets() + quantityOfInserts) / m_pTable->size() >
|
||||
10) {
|
||||
outputMessage(
|
||||
"Prohibit insertion when allocating more than 10 elements per chain",
|
||||
"red");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline void DemonstrationWidget::slotClickedMakeItems() {
|
||||
using namespace BusinessLogic::Convert;
|
||||
|
||||
bool tableTypeIsChain = getCurrentTableType() == TableType::Chain;
|
||||
if (not checkBeforeInsertion(tableTypeIsChain)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Preparing
|
||||
*/
|
||||
|
||||
if (tableTypeIsChain && m_pStandardModel->columnCount() == 0) {
|
||||
m_pStandardModel->insertColumns(0, m_sbQuantity->value() /
|
||||
m_sbBucketQuantity->value());
|
||||
}
|
||||
|
||||
m_frmProgressBar->setVisible(true);
|
||||
m_progressBar->setValue(0);
|
||||
|
||||
/*
|
||||
* For other thread
|
||||
*/
|
||||
|
||||
setEnabledInsertingProcess(false);
|
||||
m_frmProgressBar->setVisible(true);
|
||||
|
||||
auto differenceNumberOfSuccesses = m_tableInformation.m_numberOfSuccesses;
|
||||
auto differenceNumberOfFailures = m_tableInformation.m_numberOfFailures;
|
||||
|
||||
int value;
|
||||
value_type currentString = toStdArray(m_leFrom->text().toStdString());
|
||||
for (size_t progress = 0, target = m_sbQuantity->value(); progress < target;
|
||||
++progress) {
|
||||
m_pTable->insert(currentString);
|
||||
next(currentString);
|
||||
if (m_stop) {
|
||||
m_stop = false;
|
||||
break;
|
||||
}
|
||||
|
||||
value = static_cast<int>(
|
||||
progress * static_cast<double>(m_progressBar->maximum()) / target);
|
||||
if (value > m_progressBar->value()) {
|
||||
m_progressBar->setValue(value);
|
||||
}
|
||||
|
||||
QApplication::processEvents();
|
||||
}
|
||||
|
||||
updateCharts();
|
||||
updateTableView();
|
||||
|
||||
m_frmProgressBar->setVisible(false);
|
||||
setEnabledInsertingProcess(true);
|
||||
|
||||
/*
|
||||
* Output message
|
||||
*/
|
||||
|
||||
differenceNumberOfSuccesses =
|
||||
m_tableInformation.m_numberOfSuccesses - differenceNumberOfSuccesses;
|
||||
differenceNumberOfFailures =
|
||||
m_tableInformation.m_numberOfFailures - differenceNumberOfFailures;
|
||||
|
||||
m_lblOutputMessages->setText(
|
||||
getColorTextHTML(
|
||||
QString("Successful inserts: %1").arg(differenceNumberOfSuccesses),
|
||||
"green") +
|
||||
"<br>" +
|
||||
getColorTextHTML(QString("Failed inserts : %1")
|
||||
.arg(differenceNumberOfFailures),
|
||||
"red"));
|
||||
}
|
||||
|
||||
inline void DemonstrationWidget::slotClickedInsert() {
|
||||
bool tableTypeIsChain = getCurrentTableType() == TableType::Chain;
|
||||
if (not checkBeforeInsertion(tableTypeIsChain)) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_pTable->insert(toStdArray(m_leInsert->text().toStdString()));
|
||||
outputMessage(m_tableInformation.m_lastInsertion ? "Inserting is successful"
|
||||
: "The insertion has failed",
|
||||
m_tableInformation.m_lastInsertion ? "green" : "red");
|
||||
}
|
||||
|
||||
/*
|
||||
* Other logic
|
||||
*/
|
||||
|
||||
inline auto DemonstrationWidget::getCurrentFactory() const {
|
||||
using namespace BusinessLogic::Hash;
|
||||
|
||||
std::unique_ptr<const Factory::Abstract> factory;
|
||||
bool functionTypeIsFamily =
|
||||
getCurrentFunctionType() == RelationshipType::Family;
|
||||
if (getCurrentAlgorythmType() == RelationshipType::One) {
|
||||
factory = std::make_unique<Factory::One>(
|
||||
functionTypeIsFamily
|
||||
? m_complicatedFunction // The complicated function must be prepared
|
||||
: std::dynamic_pointer_cast<Function::One::Creator>(m_pFunction),
|
||||
std::dynamic_pointer_cast<Algorithm::One::Creator>(m_pAlgorithm));
|
||||
} else if (functionTypeIsFamily) {
|
||||
factory = std::make_unique<Factory::Family>(
|
||||
std::dynamic_pointer_cast<Function::Family::Creator>(m_pFunction),
|
||||
std::dynamic_pointer_cast<Algorithm::Family::Creator>(m_pAlgorithm));
|
||||
} else {
|
||||
std::invalid_argument("function type not defined");
|
||||
}
|
||||
return factory;
|
||||
}
|
||||
|
||||
inline void DemonstrationWidget::updateFunctionCreator() {
|
||||
using namespace BusinessLogic::Hash::Function;
|
||||
if (m_rbEquivalent->isChecked()) {
|
||||
m_pFunction = std::make_shared<Equivalent::Creator>();
|
||||
} else if (m_rbStandart->isChecked()) {
|
||||
m_pFunction = std::make_shared<Standart::Creator>();
|
||||
} else { // m_rbFNV1a32bit->isChecked()
|
||||
m_pFunction = std::make_shared<FNV1a::Creator>();
|
||||
}
|
||||
}
|
||||
|
||||
inline void DemonstrationWidget::updateAlgorithmCreator() {
|
||||
using namespace BusinessLogic::Hash::Algorithm;
|
||||
if (m_rbLinearProbing->isChecked()) {
|
||||
m_pAlgorithm = std::make_shared<LinearProbing::Creator>();
|
||||
} else if (m_rbQuadraticProbing->isChecked()) {
|
||||
m_pAlgorithm = std::make_shared<QuadraticProbing::Creator>();
|
||||
} else if (m_rbPseudorandomProbing->isChecked()) {
|
||||
m_pAlgorithm = std::make_shared<PseudorandomProbing::Creator>(
|
||||
m_sbFirstInputData->value());
|
||||
} else { // m_rbDoubleHashing->isChecked()
|
||||
m_pAlgorithm = std::make_shared<DoubleHashing::Creator>(
|
||||
m_sbFirstInputData->value(), m_sbSecondInputData->value());
|
||||
}
|
||||
}
|
||||
|
||||
inline void DemonstrationWidget::deleteModel() {
|
||||
auto stringListModel = m_tableView->selectionModel();
|
||||
m_tableView->setModel(nullptr);
|
||||
if (stringListModel not_eq nullptr) {
|
||||
delete stringListModel;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Table GUI
|
||||
*/
|
||||
|
||||
inline void DemonstrationWidget::slotUpdateAdditionalInputData() {
|
||||
bool predicate = {getCurrentFunctionType() == RelationshipType::Family};
|
||||
m_gbInputData->setVisible(predicate);
|
||||
m_frmFirstInputData->setVisible(predicate);
|
||||
|
||||
if (getCurrentTableType() == TableType::Chain) {
|
||||
m_rbStandart->setEnabled(true);
|
||||
m_rbEquivalent->setEnabled(true);
|
||||
m_frmSecondInputData->setVisible(false);
|
||||
} else {
|
||||
bool algorythmIsFamily =
|
||||
getCurrentAlgorythmType() == RelationshipType::Family;
|
||||
m_rbEquivalent->setDisabled(algorythmIsFamily);
|
||||
m_rbStandart->setDisabled(algorythmIsFamily);
|
||||
m_frmSecondInputData->setVisible(algorythmIsFamily);
|
||||
|
||||
if (algorythmIsFamily) {
|
||||
m_rbFNV1a32bit->setChecked(true);
|
||||
repaint();
|
||||
m_frmSecondInputData->setVisible(m_rbDoubleHashing->isChecked());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void DemonstrationWidget::slotClickedApply() {
|
||||
using namespace BusinessLogic::Hash;
|
||||
using namespace BusinessLogic::Hash::Function::Pattern;
|
||||
|
||||
if (m_rbDoubleHashing->isChecked() &&
|
||||
m_sbFirstInputData->value() == m_sbSecondInputData->value()) {
|
||||
outputMessage("Hash function coefficients should not match", "red");
|
||||
return;
|
||||
}
|
||||
|
||||
updateFunctionCreator();
|
||||
updateAlgorithmCreator();
|
||||
|
||||
bool tableTypeIsChain = getCurrentTableType() == TableType::Chain;
|
||||
bool functionTypeIsFamily =
|
||||
getCurrentFunctionType() == RelationshipType::Family;
|
||||
|
||||
if (functionTypeIsFamily && (tableTypeIsChain || getCurrentAlgorythmType() ==
|
||||
RelationshipType::One)) {
|
||||
m_complicatedFunction = std::make_shared<FamilyToOne::Creator>(
|
||||
std::dynamic_pointer_cast<Function::Family::Creator>(m_pFunction),
|
||||
m_sbFirstInputData->value());
|
||||
}
|
||||
|
||||
size_t bucketQuantity = m_sbBucketQuantity->value();
|
||||
try {
|
||||
if (tableTypeIsChain) {
|
||||
m_pTable = std::make_unique<Table::Chains>(
|
||||
bucketQuantity,
|
||||
functionTypeIsFamily
|
||||
? *m_complicatedFunction
|
||||
: *std::dynamic_pointer_cast<Function::One::Creator>(m_pFunction),
|
||||
m_alphabetPower);
|
||||
} else {
|
||||
auto factory = getCurrentFactory();
|
||||
m_pTable = std::make_unique<Table::OpenAddressing>(
|
||||
bucketQuantity, *factory, m_alphabetPower);
|
||||
}
|
||||
} catch (const std::bad_alloc &err) {
|
||||
outputMessage("Not enough RAM", "red");
|
||||
return;
|
||||
}
|
||||
|
||||
setEnabledCreateHashTable(false);
|
||||
m_pbFocus->setEnabled(true);
|
||||
m_pTable->signTheObject(this);
|
||||
|
||||
/*
|
||||
* A descendant will notify an ancestor of his own destruction when using
|
||||
* delete
|
||||
*/
|
||||
deleteModel();
|
||||
m_pStandardModel =
|
||||
new QStandardItemModel(bucketQuantity, (tableTypeIsChain ? 0 : 1), this);
|
||||
m_tableView->setModel(m_pStandardModel);
|
||||
|
||||
m_lblOutputMessages->setText(
|
||||
getColorTextHTML("The model was created", "green"));
|
||||
}
|
||||
|
||||
inline void DemonstrationWidget::slotClickedStatistics() {
|
||||
m_tableInformation.m_lastCoefficient =
|
||||
m_pTable->simpleUniformHashingCoefficient();
|
||||
QMessageBox::information(
|
||||
nullptr, "Statistics",
|
||||
QString("Maximum number of collisions:\t%1\n")
|
||||
.arg(m_tableInformation.m_maxCollision) +
|
||||
QString("Number of successful inserts:\t\t%1\n")
|
||||
.arg(m_tableInformation.m_numberOfSuccesses) +
|
||||
QString("Number of failed inserts:\t\t%1\n")
|
||||
.arg(m_tableInformation.m_numberOfFailures) +
|
||||
QString("Simple uniform hashing coefficient:\t%1\n")
|
||||
.arg(m_tableInformation.m_lastCoefficient));
|
||||
}
|
||||
|
||||
inline void DemonstrationWidget::slotClickedFindFree() {
|
||||
QString result;
|
||||
if (m_pTable->numberOfBuckets() == 0) {
|
||||
result = firstChar;
|
||||
} else {
|
||||
value_type currentString = m_tableInformation.m_lastValue;
|
||||
while (m_pTable->isExist(next(currentString)))
|
||||
continue;
|
||||
result = toQString(currentString);
|
||||
}
|
||||
|
||||
m_leFrom->setText(result);
|
||||
m_leInsert->setText(result);
|
||||
}
|
||||
|
||||
inline void DemonstrationWidget::slotClickedStop() {
|
||||
m_stop = true;
|
||||
setEnabledInsertingProcess(true);
|
||||
}
|
||||
|
||||
inline void DemonstrationWidget::slotClickedFocus() { updateCharts(); }
|
||||
|
||||
/*
|
||||
* Base GUI
|
||||
*/
|
||||
|
||||
inline void DemonstrationWidget::slotClickedReset() {
|
||||
setEnabledCreateHashTable(true);
|
||||
m_pbFocus->setDisabled(true);
|
||||
|
||||
m_complicatedFunction.reset();
|
||||
m_pTable.release();
|
||||
m_tableInformation.clear();
|
||||
resetAxes();
|
||||
deleteModel();
|
||||
|
||||
outputMessage("The model has been deleted", "green");
|
||||
}
|
||||
|
||||
inline QString
|
||||
DemonstrationWidget::getColorTextHTML(const QString &text,
|
||||
const char *const color) const {
|
||||
return QString(m_fontBeginColorHTML).arg(color) + text + m_fontEndHTML;
|
||||
}
|
||||
|
||||
inline void DemonstrationWidget::outputMessage(const QString &text,
|
||||
const char *const color) {
|
||||
m_lblOutputMessages->setText(getColorTextHTML(text, color));
|
||||
}
|
||||
|
||||
inline void DemonstrationWidget::setEnabledCreateHashTable(bool flag) {
|
||||
for (QWidget *widget : std::initializer_list<QWidget *>{
|
||||
m_pbApply, m_sbBucketQuantity, m_gbResolutionOfCollisions,
|
||||
m_gbFunction, m_gbInputData}) {
|
||||
widget->setEnabled(flag);
|
||||
}
|
||||
|
||||
if (getCurrentTableType() not_eq TableType::Chain) {
|
||||
m_gbAlgorithm->setEnabled(flag);
|
||||
}
|
||||
m_gbLoadTable->setDisabled(flag);
|
||||
m_pbStatistics->setDisabled(flag);
|
||||
}
|
||||
|
||||
inline void DemonstrationWidget::setEnabledInsertingProcess(bool flag) {
|
||||
for (QWidget *widget : std::initializer_list<QWidget *>{
|
||||
m_leFrom, m_sbQuantity, m_pbMakeItems, m_leInsert, m_pbInsert,
|
||||
m_pbReset, m_pbStatistics, m_pbFindFree}) {
|
||||
widget->setEnabled(flag);
|
||||
}
|
||||
m_pbStop->setDisabled(flag);
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
#ifndef DEMONSTRATION_WIDGET_H
|
||||
#define DEMONSTRATION_WIDGET_H
|
||||
|
||||
#include "../../Validator/validator.h"
|
||||
#include "SourceCode/BusinessLogic/businessLogic.h"
|
||||
#include "SourceCode/BusinessLogic/hashHeaderFiles.h"
|
||||
#include "hashTable.h"
|
||||
#include "ui_demonstrationWidget.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
#include <QChartView>
|
||||
#include <QLineSeries>
|
||||
#include <QStringListModel>
|
||||
#include <QtCharts>
|
||||
|
||||
class DemonstrationWidget
|
||||
: public QWidget,
|
||||
protected Ui::DemonstrationWidget,
|
||||
public BusinessLogic::Hash::Table::Pattern::InterfaceSubscriber {
|
||||
public:
|
||||
using value_type = BusinessLogic::Hash::Table::Abstract::value_type;
|
||||
using table_type = BusinessLogic::Hash::Table::Abstract;
|
||||
|
||||
private:
|
||||
struct TableInformation {
|
||||
public:
|
||||
/* Qt containers */
|
||||
QLineSeries *m_pSeriesNumberOfCollisions;
|
||||
|
||||
/* If successful */
|
||||
std::pair<size_t, size_t> m_lastCoord;
|
||||
size_t m_numberOfSuccesses;
|
||||
size_t m_lastCollision;
|
||||
size_t m_maxCollision;
|
||||
double m_lastCoefficient;
|
||||
double m_maxCoefficient;
|
||||
|
||||
/* If unsuccessful */
|
||||
size_t m_numberOfFailures;
|
||||
|
||||
/* In any case */
|
||||
value_type m_lastValue;
|
||||
bool m_lastInsertion;
|
||||
|
||||
public:
|
||||
TableInformation(const TableInformation &) = delete;
|
||||
TableInformation(TableInformation &&) = delete;
|
||||
|
||||
TableInformation(QLineSeries *maxCollision)
|
||||
: m_pSeriesNumberOfCollisions(maxCollision) {
|
||||
clear();
|
||||
};
|
||||
|
||||
public:
|
||||
void clear();
|
||||
};
|
||||
|
||||
private: // Event filter
|
||||
virtual bool eventFilter(QObject *target, QEvent *event) override {
|
||||
if (target == m_pChartCollisionsView) {
|
||||
QChartView *view = dynamic_cast<QChartView *>(target);
|
||||
QChart *chart = view->chart();
|
||||
|
||||
switch (event->type()) {
|
||||
case QEvent::Wheel: {
|
||||
QWheelEvent *currentEvent = dynamic_cast<QWheelEvent *>(event);
|
||||
|
||||
qreal factor = currentEvent->angleDelta().y() > 0 ? 0.9 : 1.1;
|
||||
QRectF rect = QRectF(chart->plotArea().left(), chart->plotArea().top(),
|
||||
chart->plotArea().width() * factor,
|
||||
chart->plotArea().height() * factor);
|
||||
|
||||
QPointF mousePos = view->mapFromGlobal(QCursor::pos());
|
||||
rect.moveCenter(mousePos);
|
||||
chart->zoomIn(rect);
|
||||
|
||||
QPointF delta = chart->plotArea().center() - mousePos;
|
||||
chart->scroll(delta.x(), -delta.y());
|
||||
|
||||
return true;
|
||||
} break;
|
||||
default: {
|
||||
return false;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
enum class TableType : short { Chain, OpenAdressing, Indefined };
|
||||
enum class RelationshipType : short { One, Family, Indefined };
|
||||
|
||||
private:
|
||||
/*
|
||||
* Constants
|
||||
*/
|
||||
static constexpr char firstChar = '!';
|
||||
static constexpr char endChar = '~';
|
||||
|
||||
QChartView *m_pChartCollisionsView;
|
||||
QStandardItemModel *m_pStandardModel;
|
||||
bool m_stop;
|
||||
|
||||
/*
|
||||
* Business logic
|
||||
*/
|
||||
std::shared_ptr<BusinessLogic::Hash::Function::Abstract::Creator> m_pFunction;
|
||||
std::shared_ptr<BusinessLogic::Hash::Function::One::Creator>
|
||||
m_complicatedFunction;
|
||||
std::shared_ptr<BusinessLogic::Hash::Algorithm::Abstract::Creator>
|
||||
m_pAlgorithm;
|
||||
size_t m_alphabetPower;
|
||||
std::unique_ptr<BusinessLogic::Hash::Table::Abstract> m_pTable;
|
||||
TableInformation m_tableInformation;
|
||||
|
||||
/*
|
||||
* HTML tags
|
||||
*/
|
||||
static constexpr auto m_fontBeginColorHTML = R"(<font color="%1">)";
|
||||
static constexpr auto m_fontEndHTML = R"(</font>)";
|
||||
|
||||
public:
|
||||
DemonstrationWidget(QWidget *pWidget = nullptr);
|
||||
|
||||
public: // Custom slots
|
||||
virtual void successfulInsertion(const table_type *, const value_type &,
|
||||
std::pair<size_t, size_t>, size_t) override;
|
||||
virtual void unsuccessfulInsertion(const table_type *,
|
||||
const value_type &) override;
|
||||
|
||||
private:
|
||||
/*
|
||||
* Business logic
|
||||
*/
|
||||
TableType getCurrentTableType() const {
|
||||
TableType result;
|
||||
result = {m_rbChains->isChecked() ? TableType::Chain
|
||||
: m_rbOpenAdressing->isChecked() ? TableType::OpenAdressing
|
||||
: TableType::Indefined};
|
||||
if (result == TableType::Indefined) {
|
||||
throw std::invalid_argument("table not defined");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
RelationshipType getCurrentFunctionType() const {
|
||||
RelationshipType result;
|
||||
result = {m_rbEquivalent->isChecked() ? RelationshipType::One
|
||||
: m_rbStandart->isChecked() ? RelationshipType::One
|
||||
: m_rbFNV1a32bit->isChecked() ? RelationshipType::Family
|
||||
: RelationshipType::Indefined};
|
||||
if (result == RelationshipType::Indefined) {
|
||||
throw std::invalid_argument("function not defined");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
RelationshipType getCurrentAlgorythmType() const {
|
||||
RelationshipType result;
|
||||
result = {m_rbLinearProbing->isChecked() ? RelationshipType::One
|
||||
: m_rbQuadraticProbing->isChecked() ? RelationshipType::One
|
||||
: m_rbPseudorandomProbing->isChecked() ? RelationshipType::Family
|
||||
: m_rbDoubleHashing->isChecked() ? RelationshipType::Family
|
||||
: RelationshipType::Indefined};
|
||||
if (result == RelationshipType::Indefined) {
|
||||
throw std::invalid_argument("algorythm not defined");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
auto getCurrentFactory() const;
|
||||
void updateFunctionCreator();
|
||||
void updateAlgorithmCreator();
|
||||
void deleteModel();
|
||||
void resetAxes() {
|
||||
m_pChartCollisionsView->chart()
|
||||
->axes(Qt::Horizontal)
|
||||
.front()
|
||||
->setRange(1, 2);
|
||||
m_pChartCollisionsView->chart()->axes(Qt::Vertical).front()->setRange(0, 1);
|
||||
}
|
||||
QString toQString(const value_type &value) {
|
||||
auto convert = value;
|
||||
unsigned size =
|
||||
std::remove(convert.begin(), convert.end(), '\0') - convert.begin();
|
||||
return QString::fromLatin1(convert.data(), size);
|
||||
}
|
||||
value_type toStdArray(std::string &&value) const {
|
||||
value_type result = {'\0', '\0', '\0', '\0', firstChar};
|
||||
std::copy(value.rbegin(), value.rend(), result.rbegin());
|
||||
return result;
|
||||
}
|
||||
value_type &next(value_type &value) const;
|
||||
bool checkBeforeInsertion(bool);
|
||||
|
||||
/*
|
||||
* GUI and displaying
|
||||
*/
|
||||
QString getColorTextHTML(const QString &text, const char *const color) const;
|
||||
void outputMessage(const QString &text, const char *const color);
|
||||
void updateColumns(size_t rowIndex);
|
||||
void updateCharts();
|
||||
void updateTableView();
|
||||
void setEnabledCreateHashTable(bool flag);
|
||||
void setEnabledInsertingProcess(bool flag);
|
||||
|
||||
public slots:
|
||||
void slotTabSwitched(int index) {
|
||||
if (index == 1) {
|
||||
QSplitter *splitter = findChild<QSplitter *>("");
|
||||
auto children = splitter->children();
|
||||
QList<int> sizes{dynamic_cast<QWidget *>(children[0])->minimumHeight(),
|
||||
dynamic_cast<QWidget *>(children[1])->minimumHeight()};
|
||||
splitter->setSizes(sizes);
|
||||
}
|
||||
}
|
||||
|
||||
private slots:
|
||||
void slotUpdateAdditionalInputData();
|
||||
void slotClickedReset();
|
||||
void slotClickedApply();
|
||||
void slotClickedMakeItems();
|
||||
void slotClickedInsert();
|
||||
void slotClickedStatistics();
|
||||
void slotClickedFindFree();
|
||||
void slotClickedStop();
|
||||
void slotClickedFocus();
|
||||
};
|
||||
|
||||
#endif // DEMONSTRATION_WIDGET_H
|
||||
@@ -0,0 +1,284 @@
|
||||
#include "testingWidget.h"
|
||||
|
||||
TestingWidget::TestingWidget(QWidget *pWidget)
|
||||
: QWidget(pWidget), m_currentComplexityName("everything"),
|
||||
m_currentTopicName("everything"), m_file("./QuestionsXML/questions.xml") {
|
||||
setupUi(dynamic_cast<QWidget *>(this));
|
||||
|
||||
connect(m_pbConfirm, &QPushButton::clicked, this,
|
||||
&TestingWidget::slotClickedConfirm);
|
||||
|
||||
connect(m_pbStart, &QPushButton::clicked, this,
|
||||
&TestingWidget::slotClickedStart);
|
||||
connect(m_pbStop, &QPushButton::clicked, this,
|
||||
&TestingWidget::slotResetTesting);
|
||||
|
||||
connect(m_rbAllDifficulties, &QRadioButton::clicked, this,
|
||||
&TestingWidget::slotSwitchedComplexity);
|
||||
connect(m_rbEasy, &QRadioButton::clicked, this,
|
||||
&TestingWidget::slotSwitchedComplexity);
|
||||
connect(m_rbMedium, &QRadioButton::clicked, this,
|
||||
&TestingWidget::slotSwitchedComplexity);
|
||||
connect(m_rbHard, &QRadioButton::clicked, this,
|
||||
&TestingWidget::slotSwitchedComplexity);
|
||||
|
||||
connect(m_rbAllTopics, &QRadioButton::clicked, this,
|
||||
&TestingWidget::slotSwitchedTopics);
|
||||
connect(m_rbDiscreteMathematics, &QRadioButton::clicked, this,
|
||||
&TestingWidget::slotSwitchedTopics);
|
||||
connect(m_rbHashFunction, &QRadioButton::clicked, this,
|
||||
&TestingWidget::slotSwitchedTopics);
|
||||
connect(m_rbHashTable, &QRadioButton::clicked, this,
|
||||
&TestingWidget::slotSwitchedTopics);
|
||||
connect(m_rbAlgorithm, &QRadioButton::clicked, this,
|
||||
&TestingWidget::slotSwitchedTopics);
|
||||
|
||||
m_frmConfirm->hide();
|
||||
}
|
||||
|
||||
inline void TestingWidget::hideCheckBoxes() {
|
||||
QCheckBox *currentCheckBox;
|
||||
for (unsigned i = 0; i < 9; ++i) {
|
||||
currentCheckBox = findChild<QCheckBox *>(QString("m_cbIndex%1").arg(i));
|
||||
currentCheckBox->hide();
|
||||
}
|
||||
}
|
||||
|
||||
inline void TestingWidget::showCheckBoxes() {
|
||||
QCheckBox *currentCheckBox;
|
||||
for (unsigned i = 0, size = m_answers.size(); i < size; ++i) {
|
||||
currentCheckBox = findChild<QCheckBox *>(QString("m_cbIndex%1").arg(i));
|
||||
currentCheckBox->show();
|
||||
currentCheckBox->setText(m_answers[i].first);
|
||||
}
|
||||
}
|
||||
|
||||
inline QString TestingWidget::getHTMLQuestion(QDomElement element) {
|
||||
QString currentQuestion;
|
||||
|
||||
element = element.firstChildElement();
|
||||
do {
|
||||
switch (getTagsEnumFromQString(element.tagName())) {
|
||||
case TagXML::Caption: {
|
||||
currentQuestion.append(QString("<h3>%1</h3>").arg(element.text()));
|
||||
} break;
|
||||
case TagXML::Text: {
|
||||
currentQuestion.append(QString(R"(
|
||||
<p align="justify">
|
||||
<var>
|
||||
<font face="Times New Roman, Times, serif">
|
||||
%2
|
||||
</font>
|
||||
</var>
|
||||
</p>
|
||||
)")
|
||||
.arg(element.text()));
|
||||
} break;
|
||||
case TagXML::Answer: {
|
||||
m_answers.append(std::make_pair(
|
||||
element.text(), element.hasAttribute("type")
|
||||
? (element.attribute("type") == "correct")
|
||||
: false));
|
||||
} break;
|
||||
case TagXML::Question: {
|
||||
throw std::invalid_argument(
|
||||
"the question cannot be nested within the question");
|
||||
} break;
|
||||
case TagXML::Undefined: {
|
||||
throw std::invalid_argument("tag undefined");
|
||||
} break;
|
||||
}
|
||||
element = element.nextSiblingElement();
|
||||
} while (not element.isNull());
|
||||
|
||||
return currentQuestion;
|
||||
}
|
||||
|
||||
inline void TestingWidget::configureInput(const QString &value) {
|
||||
hideCheckBoxes();
|
||||
resetInput();
|
||||
|
||||
if (value == "choice") {
|
||||
m_swInputData->setCurrentIndex(0);
|
||||
unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
|
||||
std::shuffle(m_answers.begin(), m_answers.end(),
|
||||
std::default_random_engine(seed));
|
||||
showCheckBoxes();
|
||||
} else if (value == "integer") {
|
||||
m_swInputData->setCurrentIndex(1);
|
||||
|
||||
} else if (value == "floatingPoint") {
|
||||
m_swInputData->setCurrentIndex(2);
|
||||
} else {
|
||||
throw std::invalid_argument("the type of the input option is undefined");
|
||||
}
|
||||
}
|
||||
|
||||
inline bool TestingWidget::showNextQuestion() {
|
||||
bool continueFlag = true;
|
||||
|
||||
auto checkType = [](const QString &value) -> bool {
|
||||
return bool{value == "choice" || value == "integer" ||
|
||||
value == "floatingPoint"};
|
||||
};
|
||||
|
||||
while (not m_currentQuestion.isNull() && continueFlag) {
|
||||
|
||||
m_currentTypeName = m_currentQuestion.attribute("type");
|
||||
if ((m_currentTopicName == "everything" ||
|
||||
m_currentQuestion.attribute("category")
|
||||
.split('|')
|
||||
.contains(m_currentTopicName)) &&
|
||||
(m_currentComplexityName == "everything" ||
|
||||
m_currentComplexityName ==
|
||||
m_currentQuestion.attribute("complexity")) &&
|
||||
checkType(m_currentTypeName)) {
|
||||
m_answers.clear();
|
||||
m_testingTextBrowser->setHtml(getHTMLQuestion(m_currentQuestion));
|
||||
|
||||
configureInput(m_currentTypeName);
|
||||
|
||||
continueFlag = false;
|
||||
}
|
||||
m_currentQuestion = m_currentQuestion.nextSiblingElement("question");
|
||||
}
|
||||
|
||||
return (not continueFlag);
|
||||
}
|
||||
|
||||
inline void TestingWidget::setEnabledGUIStartTesting(bool flag) {
|
||||
m_gbTopics->setEnabled(flag);
|
||||
m_gbDifficulty->setEnabled(flag);
|
||||
m_pbStart->setEnabled(flag);
|
||||
m_pbStop->setDisabled(flag);
|
||||
m_frmConfirm->setVisible(not flag);
|
||||
}
|
||||
|
||||
inline TestingWidget::TagXML
|
||||
TestingWidget::getTagsEnumFromQString(const QString &value) {
|
||||
TagXML result = {value == "question" ? TagXML::Question
|
||||
: value == "caption" ? TagXML::Caption
|
||||
: value == "text" ? TagXML::Text
|
||||
: value == "answer" ? TagXML::Answer
|
||||
: TagXML::Undefined};
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Slots
|
||||
*/
|
||||
|
||||
inline void TestingWidget::slotSwitchedTopics() {
|
||||
QString result = {m_rbAllTopics->isChecked() ? QString("everything")
|
||||
: m_rbDiscreteMathematics->isChecked()
|
||||
? QString("discreteMathematics")
|
||||
: m_rbHashFunction->isChecked() ? QString("hashFunction")
|
||||
: m_rbHashTable->isChecked() ? QString("hashTable")
|
||||
: m_rbAlgorithm->isChecked() ? QString("algorithm")
|
||||
: QString()};
|
||||
if (result.isNull()) {
|
||||
throw std::invalid_argument("topic undefined");
|
||||
}
|
||||
m_currentTopicName = result;
|
||||
}
|
||||
|
||||
inline void TestingWidget::slotSwitchedComplexity() {
|
||||
QString result = {m_rbAllDifficulties->isChecked() ? QString("everything")
|
||||
: m_rbEasy->isChecked() ? QString("easy")
|
||||
: m_rbMedium->isChecked() ? QString("medium")
|
||||
: m_rbHard->isChecked() ? QString("hard")
|
||||
: QString()};
|
||||
if (result.isNull()) {
|
||||
throw std::invalid_argument("complexity undefined");
|
||||
}
|
||||
m_currentComplexityName = result;
|
||||
}
|
||||
|
||||
inline bool TestingWidget::checkAnswer(const QString &value) {
|
||||
if (m_answers.size() <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result = true;
|
||||
if (value == "choice") {
|
||||
QCheckBox *currentCheckBox;
|
||||
for (unsigned i = 0, size = m_answers.size(); i < size; ++i) {
|
||||
currentCheckBox = findChild<QCheckBox *>(QString("m_cbIndex%1").arg(i));
|
||||
if (m_answers[i].second) {
|
||||
result &= currentCheckBox->isChecked();
|
||||
} else {
|
||||
result &= not currentCheckBox->isChecked();
|
||||
}
|
||||
}
|
||||
} else if (value == "integer" || value == "floatingPoint") {
|
||||
QString answer = {value == "integer" ? m_sbInteger->text()
|
||||
: m_sbDouble->text()};
|
||||
auto iter =
|
||||
std::find_if(m_answers.begin(), m_answers.end(),
|
||||
[answer](const std::pair<QString, bool> &value) -> bool {
|
||||
return (value.second == true && value.first == answer);
|
||||
});
|
||||
return (iter not_eq m_answers.end());
|
||||
} else {
|
||||
throw std::invalid_argument("input not defined");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
inline void TestingWidget::slotClickedConfirm() {
|
||||
bool result = checkAnswer(m_currentTypeName);
|
||||
emit signalReplyGiven(result);
|
||||
|
||||
if (not showNextQuestion()) {
|
||||
setEnabledGUIStartTesting(true);
|
||||
slotResetTesting();
|
||||
}
|
||||
}
|
||||
|
||||
inline void TestingWidget::slotClickedStart() {
|
||||
bool isOpened = m_file.open(QIODevice::ReadOnly);
|
||||
emit signalFileOpened(isOpened);
|
||||
|
||||
if (isOpened) {
|
||||
bool isSet = m_document.setContent(&m_file);
|
||||
m_file.close();
|
||||
emit signalContentIsSet(isSet);
|
||||
|
||||
if (isSet) {
|
||||
QDomNode questions =
|
||||
m_document.firstChild().nextSiblingElement("questions");
|
||||
if (questions.isNull()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QDomNode firstQuestion =
|
||||
questions.firstChild().nextSiblingElement("question");
|
||||
if (firstQuestion.isNull()) {
|
||||
return;
|
||||
}
|
||||
m_currentQuestion = firstQuestion.toElement();
|
||||
if (showNextQuestion()) {
|
||||
setEnabledGUIStartTesting(false);
|
||||
emit signalStartOfTesting();
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
inline void TestingWidget::resetInput() {
|
||||
QCheckBox *currentCheckBox;
|
||||
for (unsigned i = 0; i < 9; ++i) {
|
||||
currentCheckBox = findChild<QCheckBox *>(QString("m_cbIndex%1").arg(i));
|
||||
currentCheckBox->setChecked(false);
|
||||
}
|
||||
m_sbInteger->clear();
|
||||
m_sbDouble->clear();
|
||||
}
|
||||
|
||||
inline void TestingWidget::slotResetTesting() {
|
||||
m_testingTextBrowser->clear();
|
||||
setEnabledGUIStartTesting(true);
|
||||
resetInput();
|
||||
emit signalEndOfTesting();
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
#ifndef TESTING_WIDGET_H
|
||||
#define TESTING_WIDGET_H
|
||||
|
||||
#include "ui_testingWidget.h"
|
||||
|
||||
#include <random>
|
||||
|
||||
#include <QWidget>
|
||||
#include <QtXml>
|
||||
#include <QMessageBox>
|
||||
|
||||
class TestingWidget : public QWidget
|
||||
, protected Ui::TestingWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
QDomDocument m_document;
|
||||
QDomElement m_currentQuestion;
|
||||
QString m_currentComplexityName;
|
||||
QString m_currentTopicName;
|
||||
QString m_currentTypeName;
|
||||
|
||||
QFile m_file;
|
||||
QVector<std::pair<QString, bool>> m_answers;
|
||||
|
||||
private:
|
||||
enum class Topic : short {
|
||||
Everything, DiscreteMathematics, HashFunction, HashTable, Algorithm, Undefined
|
||||
};
|
||||
enum class Complexity : short {
|
||||
Everything, Easy, Medium, Hard, Undefined
|
||||
};
|
||||
enum class TagXML : short {
|
||||
Question, Caption, Text, Answer, Undefined
|
||||
};
|
||||
|
||||
public:
|
||||
TestingWidget(QWidget *pWidget = nullptr);
|
||||
~TestingWidget() {
|
||||
if(m_file.isOpen()) {
|
||||
m_file.close();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void hideCheckBoxes();
|
||||
void showCheckBoxes();
|
||||
QString getHTMLQuestion(QDomElement domNode);
|
||||
void configureInput(const QString &);
|
||||
bool showNextQuestion();
|
||||
void setEnabledGUIStartTesting(bool flag);
|
||||
TagXML getTagsEnumFromQString(const QString&);
|
||||
bool checkAnswer(const QString&);
|
||||
void resetInput();
|
||||
|
||||
private slots:
|
||||
void slotSwitchedTopics();
|
||||
void slotSwitchedComplexity();
|
||||
void slotClickedConfirm();
|
||||
void slotClickedStart();
|
||||
void slotResetTesting();
|
||||
|
||||
signals:
|
||||
void signalFileOpened(bool /*isOpen*/);
|
||||
void signalContentIsSet(bool /*isSet*/);
|
||||
void signalReplyGiven(bool /*isCorrect*/);
|
||||
void signalStartOfTesting();
|
||||
void signalEndOfTesting();
|
||||
};
|
||||
#endif // TESTING_WIDGET_H
|
||||
@@ -0,0 +1,112 @@
|
||||
#include "textBrowserWidget.h"
|
||||
|
||||
TextBrowserWidget::TextBrowserWidget(const QString &xmlFile, QWidget *pWidget)
|
||||
: QWidget(pWidget)
|
||||
{
|
||||
setupUi(dynamic_cast<QWidget *>(this));
|
||||
|
||||
connect( m_main, &QPushButton::clicked, this, &TextBrowserWidget::slotClickedMain );
|
||||
connect( m_next, &QPushButton::clicked, this, &TextBrowserWidget::slotClickedNext );
|
||||
connect( m_previous, &QPushButton::clicked, this, &TextBrowserWidget::slotClickedPrev );
|
||||
connect( m_textBrowser, &QTextBrowser::sourceChanged, this, &TextBrowserWidget::slotPageChanged );
|
||||
|
||||
m_textBrowser->setOpenExternalLinks(true);
|
||||
|
||||
unpack(xmlFile);
|
||||
}
|
||||
|
||||
/*
|
||||
* HTML theory
|
||||
*/
|
||||
|
||||
inline void TextBrowserWidget::slotPageChanged(const QUrl &url) {
|
||||
QString value = url.toString().section('/', -1);
|
||||
auto iterator = std::find_if(m_htmlPages.cbegin(), m_htmlPages.cend()
|
||||
, [value](const QString &other)->bool {
|
||||
return ( other.section('/', -1) == value );
|
||||
});
|
||||
if(iterator == m_htmlPages.cend()) {
|
||||
m_textBrowser->clear();
|
||||
return;
|
||||
}
|
||||
|
||||
m_currentIndexPage = iterator - m_htmlPages.cbegin();
|
||||
bool isMainPage = (m_currentIndexPage == 0);
|
||||
m_main->setDisabled(isMainPage);
|
||||
m_previous->setEnabled( isMainPage ? false : m_currentIndexPage not_eq 1 );
|
||||
m_next->setEnabled( isMainPage ? false : m_currentIndexPage not_eq (m_htmlPages.size() - 1) );
|
||||
}
|
||||
|
||||
inline bool TextBrowserWidget::unpack(const QString &xmlFileName) {
|
||||
QDomDocument xmlDocument;
|
||||
|
||||
QFile xmlFile(xmlFileName);
|
||||
if(not xmlFile.open(QIODevice::ReadOnly)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isSet = xmlDocument.setContent(&xmlFile);
|
||||
xmlFile.close();
|
||||
|
||||
if(not isSet) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QDomElement infoNode = xmlDocument.firstChild().nextSiblingElement("info");
|
||||
|
||||
QDomElement currentDirectoryElement = infoNode.firstChildElement("directory");
|
||||
QStringList searchPaths;
|
||||
while (not currentDirectoryElement.isNull()) {
|
||||
searchPaths.append(currentDirectoryElement.text());
|
||||
currentDirectoryElement = currentDirectoryElement.nextSiblingElement("directory");
|
||||
}
|
||||
|
||||
QDomElement currentPage = infoNode.firstChildElement("order").firstChildElement("page");
|
||||
if(currentPage.isNull()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QStringList result;
|
||||
QString currentTagName;
|
||||
bool isExists;
|
||||
while(not currentPage.isNull()) {
|
||||
currentTagName = currentPage.text();
|
||||
isExists = false;
|
||||
|
||||
for(const QString &name : searchPaths) {
|
||||
if(QDir(name).exists(currentTagName)) {
|
||||
isExists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(not isExists) {
|
||||
return false;
|
||||
}
|
||||
result.push_back(currentTagName);
|
||||
currentPage = currentPage.nextSiblingElement("page");
|
||||
}
|
||||
|
||||
m_textBrowser->setSearchPaths(searchPaths);
|
||||
m_htmlPages = result;
|
||||
|
||||
return setSourceMainPage();
|
||||
}
|
||||
|
||||
inline void TextBrowserWidget::slotClickedMain() {
|
||||
if(m_currentIndexPage not_eq 0) {
|
||||
m_textBrowser->setSource(m_htmlPages.front());
|
||||
}
|
||||
}
|
||||
|
||||
inline void TextBrowserWidget::slotClickedPrev() {
|
||||
if(m_currentIndexPage not_eq 1) {
|
||||
m_textBrowser->setSource(m_htmlPages[--m_currentIndexPage]);
|
||||
}
|
||||
}
|
||||
|
||||
inline void TextBrowserWidget::slotClickedNext() {
|
||||
if(m_currentIndexPage not_eq (m_htmlPages.size() - 1)) {
|
||||
m_textBrowser->setSource(m_htmlPages[++m_currentIndexPage]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
#ifndef THEORY_WIDGET_H
|
||||
#define THEORY_WIDGET_H
|
||||
|
||||
#include "ui_textBrowserWidget.h"
|
||||
|
||||
#include <QWidget>
|
||||
#include <QFile>
|
||||
#include <QtXml>
|
||||
|
||||
class TextBrowserWidget : public QWidget
|
||||
, protected Ui::TextBrowserWidget
|
||||
{
|
||||
private:
|
||||
/*
|
||||
* HTML
|
||||
*/
|
||||
QStringList m_htmlPages;
|
||||
int m_currentIndexPage;
|
||||
|
||||
public:
|
||||
TextBrowserWidget(const QString &xmlFile = {}
|
||||
, QWidget *pWidget = nullptr);
|
||||
|
||||
public:
|
||||
bool unpack(const QString &xmlFile);
|
||||
void setFontPointSize(int size) {
|
||||
QFont font = m_textBrowser->font();
|
||||
font.setPointSize(size);
|
||||
m_textBrowser->setFont(font);
|
||||
}
|
||||
|
||||
private:
|
||||
/*
|
||||
* Business logic
|
||||
*/
|
||||
bool setSourceMainPage() {
|
||||
if(not m_htmlPages.isEmpty()) {
|
||||
m_textBrowser->setSource( QUrl( m_htmlPages.front() ) );
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private slots:
|
||||
void slotPageChanged(const QUrl &url);
|
||||
void slotClickedMain();
|
||||
void slotClickedPrev();
|
||||
void slotClickedNext();
|
||||
};
|
||||
#endif // THEORY_WIDGET_H
|
||||
Reference in New Issue
Block a user