diff --git a/CHANGELOG b/CHANGELOG index 889138d..c2cf6ad 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -6,6 +6,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] ### Added +- added profiles dialog +### Fixed +- start seqnece continues in a buggy way when cancel is being pressed while 'at your marks' or 'ready' +- bug that made the start sequence freeze if a delay of zero or lower or a non valid number was set as delay + +## [0.03 - BETA] - 2018-07-29 +### Added - cancel button during start sequence - new screen in landscape mode - buttons for settings and profiles diff --git a/android-sources/AndroidManifest.xml b/android-sources/AndroidManifest.xml index bec9b3f..20f173d 100644 --- a/android-sources/AndroidManifest.xml +++ b/android-sources/AndroidManifest.xml @@ -1,5 +1,5 @@ - + diff --git a/graphics/Buzzer.xcf b/graphics/Buzzer.xcf new file mode 100644 index 0000000..388afd7 Binary files /dev/null and b/graphics/Buzzer.xcf differ diff --git a/headers/appsettings.h b/headers/appsettings.h index b5459ec..8e315ae 100644 --- a/headers/appsettings.h +++ b/headers/appsettings.h @@ -14,12 +14,14 @@ public: Q_INVOKABLE QString loadSetting(const QString &key); Q_INVOKABLE void writeSetting(const QString &key, const QVariant &variant); + Q_INVOKABLE void setDefaultSetting(const QString &key, const QVariant &defaultVariant); QSettings *settingsManager; signals: public slots: + }; extern AppSettings * pGlobalAppSettings; diff --git a/headers/buzzerconn.h b/headers/buzzerconn.h index 8db5c6b..f20454e 100644 --- a/headers/buzzerconn.h +++ b/headers/buzzerconn.h @@ -35,8 +35,8 @@ private: signals: public slots: - ReturnData_t senddata(QUrl serviceUrl); - Q_INVOKABLE QList gettimes(); + ReturnData_t senddata(QUrl serviceUrl, int timeout); + Q_INVOKABLE QList gettimes(int timeout); Q_INVOKABLE bool connect(); Q_INVOKABLE bool calcoffset(); Q_INVOKABLE bool buzzer_triggered(); diff --git a/headers/sqlstoragemodel.h b/headers/sqlstoragemodel.h index d756a92..51289f3 100644 --- a/headers/sqlstoragemodel.h +++ b/headers/sqlstoragemodel.h @@ -2,14 +2,22 @@ #define SQLSTORAGEMODEL_H #include +#include +#include +#include +#include +#include +#include -class SqlStorageModel : public QObject +class SqlStorageModel : public QSqlTableModel { Q_OBJECT public: explicit SqlStorageModel(QObject *parent = nullptr); + QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE; + QHash roleNames() const Q_DECL_OVERRIDE; signals: public slots: diff --git a/qml/ProfilesDialog.qml b/qml/ProfilesDialog.qml new file mode 100644 index 0000000..9774245 --- /dev/null +++ b/qml/ProfilesDialog.qml @@ -0,0 +1,182 @@ +import QtQuick 2.9 +import QtMultimedia 5.8 +import QtQuick.Window 2.2 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.3 +import com.itsblue.speedclimbingstopwatch 1.0 + + +Popup { + id: root + x: startButt.x + y: startButt.y + width: startButt.width + height: startButt.height + modal: true + dim: false + + enter: Transition { + NumberAnimation { properties: "scale"; from: 0; to: 1; duration: 300; easing.type: Easing.Linear } + } + + exit: Transition { + NumberAnimation { properties: "scale"; from: 1; to: 0; duration: 300; easing.type: Easing.Linear } + } + + background: Rectangle { + radius: width * 0.5 + color: "white" + border.color: "grey" + border.width: 1 + + Label { + id: head_text + text: options_stack.currentItem.title + font.pixelSize: headlineUnderline.width * 0.1 + anchors { + horizontalCenter: parent.horizontalCenter + top: parent.top + topMargin: headlineUnderline.anchors.topMargin / 2 - height / 2 + } + } + + Rectangle { + id: headlineUnderline + height: 1 + width: parent.width + color: "grey" + anchors { + top: parent.top + left: parent.left + right: parent.right + topMargin: parent.height * 0.15 + rightMargin: parent.radius - Math.sqrt(Math.pow(parent.radius,2)-Math.pow(parent.radius-anchors.topMargin,2)) + leftMargin: parent.radius - Math.sqrt(Math.pow(parent.radius,2)-Math.pow(parent.radius-anchors.topMargin,2)) + } + } + + ProgressCircle { + id: prog + property string text: "connecting.." + anchors.fill: parent + opacity: 0 + lineWidth: 5 + + arcBegin: 0 + arcEnd: 0 + + Timer { + id: prog_refresh + running: false + interval: 1 + repeat: true + onTriggered: { + prog.arcEnd = 360 * ( _cppBuzzerConn.get("connection_progress") / 100 ) + } + } + + Label { + id: content + text: parent.text + anchors.centerIn: parent + font.pixelSize: parent.width * 0.1 + + } + } + + Button { + id: head_back + + anchors { + left: parent.left + leftMargin: parent.width * 0.17 + top:parent.top + topMargin: parent.height * 0.025 + } + height: parent.height * 0.1 + width:height + + background: Rectangle { + radius: width * 0.5 + color: "white" + border.color: "grey" + border.width: 1 + Image { + anchors.fill: parent + anchors.margins: parent.width * 0.2 + source: "qrc:/graphics/icons/back_black.png" + + } + } + + onClicked: { + options_stack.depth > 1 ? options_stack.pop():root.close() + } + + onPressedChanged: { + if(pressed){ + background.color = "lightgrey" + } + else { + background.color = "white" + } + } + Behavior on opacity { + NumberAnimation { + duration: 100 + } + } + } + } + + ListView { + id: profileList + anchors { + top: parent.top + left: parent.left + leftMargin: ( parent.width - headlineUnderline.width ) / 2 + right: parent.right + rightMargin: headlineUnderline.anchors.rightMargin + topMargin: headlineUnderline.anchors.topMargin + bottom: parent.bottom + } + model:SqlProfileModel{} + + delegate: SwipeDelegate { + id: swipeDelegate + text: model.name + + width: profileList.width + Component { + id: component + + Rectangle { + color: mouse.pressed ? "#333" : "#444" + width: parent.width + height: parent.height + clip: true + + + Label { + text: qsTr("Press me!") + color: "#21be2b" + anchors.centerIn: parent + + } + + MouseArea { + id: mouseAr + anchors.fill: parent + onClicked: { + model.remove(index) + } + } + } + } + + swipe.left: component + swipe.right: component + } + ScrollIndicator.vertical: ScrollIndicator { } + } +} diff --git a/qml/SettingsDialog.qml b/qml/SettingsDialog.qml index 6bb7d98..bbbe391 100644 --- a/qml/SettingsDialog.qml +++ b/qml/SettingsDialog.qml @@ -53,6 +53,35 @@ Popup { } } + ProgressCircle { + id: prog + property string text: "connecting.." + anchors.fill: parent + opacity: 0 + lineWidth: 5 + + arcBegin: 0 + arcEnd: 0 + + Timer { + id: prog_refresh + running: false + interval: 1 + repeat: true + onTriggered: { + prog.arcEnd = 360 * ( _cppBuzzerConn.get("connection_progress") / 100 ) + } + } + + Label { + id: content + text: parent.text + anchors.centerIn: parent + font.pixelSize: parent.width * 0.1 + + } + } + Button { id: head_back @@ -98,36 +127,6 @@ Popup { } } - ProgressCircle { - id: prog - property string text: "connecting.." - anchors.centerIn: parent - size: parent.height * 1.03 - opacity: 0 - lineWidth: 5 - - arcBegin: 0 - arcEnd: 0 - - Timer { - id: prog_refresh - running: false - interval: 1 - repeat: true - onTriggered: { - prog.arcEnd = 360 * ( _cppBuzzerConn.get("connection_progress") / 100 ) - } - } - - Label { - id: content - text: parent.text - anchors.centerIn: parent - font.pixelSize: parent.width * 0.1 - - } - } - StackView { id: options_stack property int text_pixelSize: headlineUnderline.width * 0.08 diff --git a/qml/main.qml b/qml/main.qml index 539f2d7..311a0bc 100644 --- a/qml/main.qml +++ b/qml/main.qml @@ -118,12 +118,11 @@ Window { source: "qrc:/sounds/at_marks_1.wav" onPlayingChanged: { - if(!playing){ + if(!playing && root.state==="STARTING"){ if(_cppAppSettings.loadSetting("ready_en") === "true"){ next_actionTimer.action = "ready" - next_actionTimer.interval = _cppAppSettings.loadSetting("ready_delay") + next_actionTimer.interval = _cppAppSettings.loadSetting("ready_delay")>0 ? _cppAppSettings.loadSetting("ready_delay"):1 next_actionTimer.start() - //readySound.play() } else{ startSound.play() @@ -136,7 +135,7 @@ Window { id: readySound source: "qrc:/sounds/ready_1.wav" onPlayingChanged: { - if(!playing){ + if(!playing && root.state==="STARTING"){ startSound.play() } } @@ -148,7 +147,7 @@ Window { source: "qrc:/sounds/OFFICAL_IFSC_STARTIGNAL.wav" onPlayingChanged: { - if(!playing){ + if(!playing && root.state==="STARTING"){ root.startTime = _cppBuzzerConn.get("currtime") _cppBuzzerConn.start() root.currTime = _cppBuzzerConn.get("currtime") @@ -183,6 +182,20 @@ Window { } Rectangle { + anchors { + top: parent.top + topMargin: 10 + left: parent.left + leftMargin: 10 + } + + width: height + radius: height*0.5 + color: "#6efc0f" + } + + Rectangle { + id: upper_line width: root.landscape() ? 1:parent.width height: root.landscape() ? parent.height:1 color: "grey" @@ -242,13 +255,13 @@ Window { root.state = "STARTING" if(_cppAppSettings.loadSetting("at_marks_en") === "true"){ next_actionTimer.action = "at_marks" - next_actionTimer.interval = _cppAppSettings.loadSetting("at_marks_delay") + next_actionTimer.interval = _cppAppSettings.loadSetting("at_marks_delay")>0 ? _cppAppSettings.loadSetting("at_marks_delay"):1 next_actionTimer.start() return } if(_cppAppSettings.loadSetting("ready_en") === "true"){ next_actionTimer.action = "ready" - next_actionTimer.interval = _cppAppSettings.loadSetting("ready_delay") + next_actionTimer.interval = _cppAppSettings.loadSetting("ready_delay")>0 ? _cppAppSettings.loadSetting("ready_delay"):1 next_actionTimer.start() return } @@ -320,13 +333,13 @@ Window { } onClicked: { + root.stoppedTime = 0 + time.text = "false start" + root.state = "STOPPED" next_actionTimer.stop() at_marksSound.stop() readySound.stop() startSound.stop() - root.stoppedTime = 0 - time.text = "false start" - root.state = "STOPPED" } Behavior on scale { @@ -353,9 +366,12 @@ Window { Popups ------*/ SettingsDialog{ - id:settingsDialog + id: settingsDialog } + ProfilesDialog { + id: profilesDialog + } /*------------------- lower line and menu @@ -455,6 +471,7 @@ Window { } onClicked: { + profilesDialog.open() } background: Rectangle { @@ -475,83 +492,6 @@ Window { } } - - /* - // ComboBox { - // id: profileBox - // property int profileIndex: -1 - // model: SqlProfileModel{} - // textRole: "name" - // width: parent.width - // anchors { - // bottom: parent.bottom - // horizontalCenter: parent.horizontalCenter - // } - // height: parent.height * 0.05 - // background: Rectangle { - // color: profileBox.down ? "lightgrey":"white" - // border.width: 1 - // border.color: "grey" - // } - // popup: Popup { - // id: profileBox_popup - // property bool popup_open: false - // y: profileBox.height - 1 - // width: profileBox.width - // implicitHeight: contentItem.implicitHeight - // padding: 1 - - // contentItem: ListView { - // clip: true - // implicitHeight: contentHeight - // model: profileBox.popup.visible ? profileBox.delegateModel : null - // currentIndex: profileBox.highlightedIndex - - // ScrollIndicator.vertical: ScrollIndicator { } - // } - // onVisibleChanged: { - // if(visible){ - // if(popup_open){ - // popup_open = false - // return - // } - // popup_open = true - // height=implicitHeight - // } - // else { - // height = 0 - // visible = true - // } - // } - - // background: Rectangle { - // border.color: "grey" - // radius: 2 - // } - - // Behavior on height { - // NumberAnimation - // { - // onRunningChanged: { - // if(!running && !profileBox_popup.popup_pen){ - // profileBox_popup.popup_open = false - // profileBox_popup.visible = false - // } - // } - - // duration: 200 - // } - // } - // } - - // onDownChanged: { - // if(profileIndex !== currentIndex) - // profileIndex = currentIndex - // } - // } - */ - - /*---------------------- Timer states ----------------------*/ diff --git a/qml/qml.qrc b/qml/qml.qrc index 4c70231..9435bdc 100644 --- a/qml/qml.qrc +++ b/qml/qml.qrc @@ -4,5 +4,6 @@ FadeAnimation.qml SettingsDialog.qml ProgressCircle.qml + ProfilesDialog.qml diff --git a/sources/appsettings.cpp b/sources/appsettings.cpp index 4ea3e9c..20e3314 100644 --- a/sources/appsettings.cpp +++ b/sources/appsettings.cpp @@ -4,8 +4,13 @@ AppSettings::AppSettings(QObject* parent) :QObject(parent) { QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); - qDebug(path.toLatin1()); + this->settingsManager = new QSettings(path+"/settings.ini", QSettings::IniFormat); + + this->setDefaultSetting("ready_en", "false"); + this->setDefaultSetting("ready_delay", 0); + this->setDefaultSetting("at_marks_en", "false"); + this->setDefaultSetting("at_marks_delay", 0); } QString AppSettings::loadSetting(const QString &key) @@ -23,6 +28,15 @@ void AppSettings::writeSetting(const QString &key, const QVariant &variant) this->settingsManager->endGroup(); } +void AppSettings::setDefaultSetting(const QString &key, const QVariant &defaultVariant) +{ + QString value = this->loadSetting(key); + if(value == "false"){ + this->writeSetting(key, defaultVariant); + } + +} + AppSettings::~AppSettings() { delete settingsManager; diff --git a/sources/buzzerconn.cpp b/sources/buzzerconn.cpp index 8927273..e390163 100644 --- a/sources/buzzerconn.cpp +++ b/sources/buzzerconn.cpp @@ -12,7 +12,7 @@ BuzzerConn::BuzzerConn(QObject *parent) : QObject(parent) bool BuzzerConn::connect() { qDebug() << "connecting to buzzer..."; - QList times = gettimes(); + QList times = gettimes(1000); qDebug() << times[0]; if(times[0] == 200.0){ this->connected = true; @@ -34,7 +34,7 @@ bool BuzzerConn::connect() bool BuzzerConn::calcoffset() { - QList times = gettimes(); + QList times = gettimes(1000); if(times.length() != 3){ return(false); } @@ -59,10 +59,10 @@ bool BuzzerConn::calcoffset() } } -QList BuzzerConn::gettimes() +QList BuzzerConn::gettimes(int timeout) { QList times; - ReturnData_t ret = senddata(QUrl("http://192.168.4.1")); + ReturnData_t ret = senddata(QUrl("http://192.168.4.1"), timeout); times.append(double(ret.status_code)); if(ret.status_code == 200){ @@ -85,7 +85,7 @@ bool BuzzerConn::buzzer_triggered() if(!this->connected){ return(false); } - QList times = this->gettimes(); + QList times = this->gettimes(1000); if(times[0] == 200.0){ if(times[2] > this->latest_button_pressed){ this->latest_button_pressed = times[2]; @@ -97,7 +97,7 @@ bool BuzzerConn::buzzer_triggered() } } else{ - this->connected = false; + //this->connected = false; return(false); } } @@ -107,7 +107,7 @@ bool BuzzerConn::start() if(!this->connected){ return(false); } - QList times = this->gettimes(); + QList times = this->gettimes(1000); if(times[0] == 200.0 && this->connected){ this->latest_button_pressed = times[2]; return(true); @@ -142,11 +142,11 @@ double BuzzerConn::get(QString key) QString BuzzerConn::test() { - ReturnData_t ret = this->senddata(QUrl("http://www.google.de")); + ReturnData_t ret = this->senddata(QUrl("http://www.google.de"), 500); return(ret.text); } -ReturnData_t BuzzerConn::senddata(QUrl serviceUrl) +ReturnData_t BuzzerConn::senddata(QUrl serviceUrl, int timeout) { ReturnData_t ret; //this is a custom type to store the returned data @@ -168,7 +168,7 @@ ReturnData_t BuzzerConn::senddata(QUrl serviceUrl) loop.connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit())); loop.connect(this->networkManager, SIGNAL(finished(QNetworkReply*)), SLOT(quit())); - timer.start(500); + timer.start(timeout); reply = this->networkManager->post(request, pdata.toString(QUrl::FullyEncoded).toUtf8()); loop.exec(); timer.stop(); diff --git a/sources/sqlprofilemodel.cpp b/sources/sqlprofilemodel.cpp index 936a38b..46c8287 100644 --- a/sources/sqlprofilemodel.cpp +++ b/sources/sqlprofilemodel.cpp @@ -23,7 +23,8 @@ static void createTable() "CREATE TABLE IF NOT EXISTS `times` (" " `id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE," " `profileid` INTEGER NOT NULL," - " `time` INTEGER NOT NULL" + " `time` INTEGER NOT NULL, " + " `timestamp` INTEGER NOT NULL" " );")) { qFatal("Failed to query database: %s", qPrintable(query.lastError().text())); } diff --git a/sources/sqlstoragemodel.cpp b/sources/sqlstoragemodel.cpp index 925713e..8eeeca3 100644 --- a/sources/sqlstoragemodel.cpp +++ b/sources/sqlstoragemodel.cpp @@ -1,6 +1,26 @@ #include "headers/sqlstoragemodel.h" -SqlStorageModel::SqlStorageModel(QObject *parent) : QObject(parent) +SqlStorageModel::SqlStorageModel(QObject *parent) : QSqlTableModel(parent) { - + qDebug("ProfileModel constructor"); + setTable("times"); + select(); +} + +QVariant SqlStorageModel::data(const QModelIndex &index, int role) const +{ + if (role < Qt::UserRole) + return QSqlTableModel::data(index, role); + + const QSqlRecord sqlRecord = record(index.row()); + return sqlRecord.value(role - Qt::UserRole); +} + +QHash SqlStorageModel::roleNames() const +{ + QHash names; + names[Qt::UserRole + 0] = "id"; + names[Qt::UserRole + 1] = "name"; + + return names; } diff --git a/speedclimbing_stopwatch.pro b/speedclimbing_stopwatch.pro index e6ae4e9..f266bdc 100644 --- a/speedclimbing_stopwatch.pro +++ b/speedclimbing_stopwatch.pro @@ -3,6 +3,7 @@ QT += quick sql android { QT += androidextras } +VERSION =0.03 CONFIG += c++11 # The following define makes your compiler emit warnings if you use @@ -44,7 +45,8 @@ else: unix:!android: target.path = /opt/$${TARGET}/bin DISTFILES += \ android-sources/AndroidManifest.xml \ - android-sources/src/StayAwake.java + android-sources/src/StayAwake.java \ + CHANGELOG android { ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android-sources