Наткнулся на посты, где очень бурно обсуждалась тема эффективного счета в уме
Один, Два
Тема заинтересовала и я начал искать программы и сервисы для тренировки
В скором времени пришло осознание, что быстрее написать свое приложение, под свои хотелки и с кнопками где мне удобно, + перспектива переноса на любимый Windows Phone
Для тех кому интересно посмотреть/покритиковать — добро пожаловать под кат
Список классов:
- controller.cpp — реализует логику переключения состояния, полный MVVP наверно не совсем уместен на сигналах слотах, да на таком простом проекте, по этому просто абстрагирует от основного View
controller.cpp
void Controller::addButtonControl(QPushButton *pbutton, eCurrentState state) { buttons.push_back(qMakePair(pbutton, state)); } void Controller::addResultInput(QLineEdit *pline) { resultlabelInput = pline; } void Controller::changeState(QPushButton *pbutton) { resetCheckedAllExectThis(pbutton); if(is_activeButton(pbutton)) { pbutton->setChecked(true); setCurrentState(getStateItem(pbutton)); } }
- DarkStyle.cpp — набор css из проекта DarkStyle
- exercise.cpp — генерирует примеры, использует random до заданных разрядов, проверяет деление на NULL
exercise.cpp
Exercise::sExercise Exercise::getLastExercise() { return exercise; } Exercise::sExercise Exercise::getNewExercise( Controller::eCurrentState operation, int count_max, bool use_minus) { switch(operation) { case Controller::eCurrentState::state_statisctic: break; case Controller::eCurrentState::state_settings: break; case Controller::eCurrentState::state_substraction: exercise.first = random->getRandom(count_max); exercise.last = random->getRandom(count_max); if(!use_minus) { while(exercise.first < 0) { exercise.first = random->getRandom(count_max); } while(exercise.last < 0) { exercise.last = random->getRandom(count_max); } } exercise.result = (exercise.first - exercise.last); break; case Controller::eCurrentState::state_addtion: exercise.first = random->getRandom(count_max); exercise.last = random->getRandom(count_max); if(!use_minus) { while(exercise.first < 0) { exercise.first = random->getRandom(count_max); } while(exercise.last < 0) { exercise.last = random->getRandom(count_max); } } exercise.result = (exercise.first + exercise.last); break; case Controller::eCurrentState::state_division: exercise.first = random->getRandom(count_max); exercise.last = random->getRandom(count_max); while(exercise.last <= 0) { exercise.last = random->getRandom(count_max); } if(!use_minus) { while(exercise.first < 0) { exercise.first = random->getRandom(count_max); } } exercise.result = (exercise.first / exercise.last); break; case Controller::eCurrentState::state_multiplication: exercise.first = random->getRandom(count_max); exercise.last = random->getRandom(count_max); if(!use_minus) { while(exercise.first < 0) { exercise.first = random->getRandom(count_max); } while(exercise.last < 0) { exercise.last = random->getRandom(count_max); } } exercise.result = (exercise.first * exercise.last); break; } return exercise; }
- graphclass.cpp симпатичный класс для диаграмм QCustomplot, лицензия GPL
Код ничем не примечателен, почти целиком из примеров qcustomplot.graphclass.cppvoid GraphClass::init() { QLinearGradient gradient(0, 0, 0, 400); gradient.setColorAt(0, QColor(90, 90, 90)); graph->setBackground(QBrush(gradient)); graph->addGraph(); // blue line graph->graph(0)->setPen(QPen(QBrush(QColor(40, 110, 255)), Qt::Dense5Pattern)); QSharedPointer<QCPAxisTickerTime> timeTicker(new QCPAxisTickerTime); timeTicker->setTimeFormat("%h:%m:%s"); graph->xAxis->setTicker(timeTicker); graph->graph(0)->rescaleValueAxis(true, true); graph->xAxis->setVisible(true); graph->yAxis->setVisible(true); graph->xAxis->setTicker(timeTicker); graph->axisRect()->setupFullAxesBox(); graph->yAxis->setRange(-1.2, 1.2); graph->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectLegend); // make left and bottom axes transfer their ranges to right and top axes: connect(graph->xAxis, SIGNAL(rangeChanged(QCPRange)), graph->xAxis2, SLOT(setRange(QCPRange))); connect(graph->yAxis, SIGNAL(rangeChanged(QCPRange)), graph->yAxis2, SLOT(setRange(QCPRange))); } void GraphClass::addNewValue(double value) { lastValue = value; } void GraphClass::update() { static QTime time(QTime::currentTime()); // calculate two new data points: double key = time.elapsed()/10000.0; // time elapsed since start of demo, in seconds static double lastPointKey = 0; if (key-lastPointKey > 0.002) // at most add point every 2 ms { // add data to lines: graph->graph(0)->addData(key, lastValue); lastPointKey = key; } // make key axis range scroll with the data (at a constant range size of 8): graph->xAxis->setRange(key, 8, Qt::AlignRight); graph->replot(); }
- settings.cpp — настройки, JSON (QJsonObject, QJsonDocument)
settings.cpp
bool Settings::loadJson() { QString val; QFile file; file.setFileName("./settings.json"); file.open(QIODevice::ReadWrite | QIODevice::Text); val = file.readAll(); file.close(); if(!val.isEmpty()) { QJsonDocument d = QJsonDocument::fromJson(val.toUtf8()); QJsonObject sett2 = d.object(); QJsonValue value = sett2.value(QString("settings")); qDebug() << value; QJsonObject item = value.toObject(); //-- settings QJsonValue subobj = item["count_digits"]; count_digit_max = subobj.toInt(); qDebug() << subobj.toString(); subobj = item["use_minus"]; use_minus = static_cast<bool>(subobj.toInt()); qDebug() << subobj.toString(); subobj = item["first_name"]; first_name = subobj.toString(); qDebug() << subobj.toString(); subobj = item["last_name"]; last_name = subobj.toString(); qDebug() << subobj.toString(); subobj = item["exercise_all_time"]; exercise_all_time = new QTime(); *exercise_all_time = exercise_all_time->fromString(subobj.toString(), "HH:mm:ss"); qDebug() << exercise_all_time->toString("HH:mm:ss"); subobj = item["exercise_correct"]; exercise_correct = subobj.toInt(); qDebug() << subobj.toString(); subobj = item["exercise_wrong"]; exercise_wrong = subobj.toInt(); qDebug() << subobj.toString(); subobj = item["use_advice"]; use_advice = static_cast<bool>(subobj.toInt()); qDebug() << subobj.toString(); subobj = item["exercise_passed"]; exercise_passed = subobj.toInt(); qDebug() << subobj.toString(); //-- advice value = sett2.value(QString("advice")); qDebug() << value; item = value.toObject(); for(auto it=item.begin(); it!=item.end(); it++) { advice.push_back((*it).toString()); qDebug() << (*it).toString(); } } return !val.isEmpty(); } void Settings::saveJson() { QFile file; file.setFileName("./settings.json"); if(file.open(QIODevice::ReadWrite | QIODevice::Text)){ QJsonObject jsonObSettings; QJsonObject jsonObParamSettings; QJsonObject jsonObParamAdvice; jsonObParamSettings["count_digits"] = count_digit_max; jsonObParamSettings["use_minus"] = use_minus; jsonObParamSettings["first_name"] = first_name; jsonObParamSettings["last_name"] = last_name; jsonObParamSettings["exercise_all_time"] = exercise_all_time->toString("HH:mm:ss"); jsonObParamSettings["exercise_correct"] = exercise_correct; jsonObParamSettings["exercise_wrong"] = exercise_wrong; jsonObParamSettings["exercise_passed"] = exercise_passed; jsonObParamSettings["use_advice"] = use_advice; int i=0; for(auto it=advice.begin(); it!=advice.end(); it++) { jsonObParamAdvice[QString("advice_%1").arg(i)] = (*it); i++; } jsonObSettings["settings"] = jsonObParamSettings; jsonObSettings["advice"] = jsonObParamAdvice; QJsonDocument doc(jsonObSettings); file.write(doc.toJson()); file.close(); } }
- statistic.cpp — просто хранит сколько примеров решено, ошибки и т.п
- timer.cpp слушит для упорядочивания таймеров (1сек. для счетчика, 25сек. для советов, 15сек. для сохранения результатов в JSON (пока так, вообще нужен SqLite
timer.cpp
QTimer *timer_update_result = new QTimer(); time = new QTime(0, 0, 0, 0); timer_one_sec = new QTimer(); timer_get_advice = new QTimer(); connect(timer_one_sec, SIGNAL(timeout()), this, SLOT(nextTimeOneSecond())); connect(timer_get_advice, SIGNAL(timeout()), this, SLOT(nextTimeAdviceTime())); connect(timer_update_result, SIGNAL(timeout()), this, SLOT(nextTimeUpdateResult())); QTimer::singleShot(100, [=] { nextTimeAdviceTime(); }); timer_update_result->start(15000); timer_one_sec->start(1000); timer_get_advice->start(25000); void TimerClass::nextTimeOneSecond() { QString time_string; if(is_enabled) { *time = (time->addSecs(1)); time_string = time->toString("hh:mm:ss"); emit update(time_string); } } void TimerClass::nextTimeUpdateResult() { emit updateLastResult(); } void TimerClass::nextTimeAdviceTime() { emit updateAdvice(); }
- view.cpp — основной класс, самый объемный
view.cpp - конструктор
//-- create statistic class statistict = new Statistic(ui->graph, ui->stat_all_time, ui->stat_exe_correct, ui->stat_exe_passed, ui->stat_exe_wrong); //--- create setttings settings = new Settings(); if(settings->init()) { statistict->setLastStatistic( settings->getLastAllTime(), settings->getLastExerciseCorect(), settings->getLastExerciseWrong(), settings->getLastExercisePassed()); } else { QMessageBox::critical(this, "Error JSON", "Error opening JSON file!", QMessageBox::Ok); } //-- create controller controller = new Controller(); //--- create exercise exercise = new Exercise(); //-- create reverse timerClass and connect slot timer = new TimerClass(); connect(timer, SIGNAL(update(QString)), this, SLOT(updateTime(QString))); connect(timer, SIGNAL(updateAdvice()), this, SLOT(updateAdvice())); connect(timer, SIGNAL(updateLastResult()), this, SLOT(updateLastResult())); //-- Set StyleSheet QFile styleF; styleF.setFileName("./darkstyle/darkstyle.qss"); styleF.open(QFile::ReadOnly); QString qssStr = styleF.readAll(); qApp->setStyleSheet(qssStr); //--- insert control buttons controller->addButtonControl(ui->addition_button, Controller::state_addtion); controller->addButtonControl(ui->division_button, Controller::state_division); controller->addButtonControl(ui->multiplication_button, Controller::state_multiplication); controller->addButtonControl(ui->settings_button, Controller::state_settings); controller->addButtonControl(ui->statistic_button, Controller::state_statisctic); controller->addButtonControl(ui->substraction_button, Controller::state_substraction); controller->addResultInput(ui->result_input); ui->settings_count_digits->setCurrentIndex(settings->getCountDigiMax()-1); ui->settings_use_minus->setCurrentIndex(settings->getUseMinus()); ui->settings_first_name->setText(settings->getFirstName()); ui->settings_last_name->setText(settings->getLastName()); ui->settings_use_advice->setCurrentIndex(settings->getUseAdvice()); //-- Connect signal-slots connect(ui->result_input, SIGNAL(returnPressed()), this, SLOT(resultInputEditFinished())); connect(controller, SIGNAL(changeStateAddition()), this, SLOT(updateStateAddition())); connect(controller, SIGNAL(changeStateMultiplication()), this, SLOT(updateStateMultiplication())); connect(controller, SIGNAL(changeStateSettings()), this, SLOT(updateStateSettings())); connect(controller, SIGNAL(changeStateStatistic()), this, SLOT(updateStateStatistic())); connect(controller, SIGNAL(changeStateSubstraction()), this, SLOT(updateStateSubstraction())); connect(controller, SIGNAL(changeStateDivision()), this, SLOT(updateStateDivision())); connect(ui->settings_count_digits, SIGNAL(currentIndexChanged(int)), this, SLOT(updateCountDigits(int))); connect(ui->settings_use_minus, SIGNAL(currentIndexChanged(int)), this, SLOT(updateUseMinus(int))); connect(ui->settings_use_advice, SIGNAL(currentIndexChanged(int)), this, SLOT(updateUseAdvice(int))); connectSlots();
Ссылка на gitHub
Автор: Владимир