see changelog
This commit is contained in:
parent
04293363e2
commit
3cfca3a083
15 changed files with 561 additions and 81 deletions
|
@ -5,6 +5,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
||||||
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
### Added
|
||||||
|
- cancel button during start sequence
|
||||||
|
- new screen in landscape mode
|
||||||
|
- buttons for settings and profiles
|
||||||
|
|
||||||
## [0.02] - 2018-07-18
|
## [0.02] - 2018-07-18
|
||||||
### Fixed
|
### Fixed
|
||||||
|
@ -17,4 +21,4 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||||
prevent it from getting out of the screen
|
prevent it from getting out of the screen
|
||||||
|
|
||||||
## [0.01]
|
## [0.01]
|
||||||
### Initial Release
|
### Initial Release
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<manifest package="com.itsblue.speedclimbing_stopwatch" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="0.02" android:versionCode="2" android:installLocation="auto">
|
<manifest package="com.itsblue.speedclimbing_stopwatchtest" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="0.02" android:versionCode="2" android:installLocation="auto">
|
||||||
<application android:hardwareAccelerated="true" android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="speedclimbing stw" android:icon="@drawable/icon">
|
<application android:hardwareAccelerated="true" android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="speedclimbing stw" android:icon="@drawable/icon">
|
||||||
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation" android:name="org.qtproject.qt5.android.bindings.QtActivity" android:label="speedclimbing stw" android:screenOrientation="unspecified" android:launchMode="singleTop">
|
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation" android:name="com.itsblue.StayAwake" android:label="speedclimbing stw" android:screenOrientation="unspecified" android:launchMode="singleTop">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN"/>
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
<category android:name="android.intent.category.LAUNCHER"/>
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
|
@ -59,6 +59,7 @@
|
||||||
-->
|
-->
|
||||||
<meta-data android:name="android.app.extract_android_style" android:value="full"/>
|
<meta-data android:name="android.app.extract_android_style" android:value="full"/>
|
||||||
<!-- extract android style -->
|
<!-- extract android style -->
|
||||||
|
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<!-- For adding service(s) please check: https://wiki.qt.io/AndroidServices -->
|
<!-- For adding service(s) please check: https://wiki.qt.io/AndroidServices -->
|
||||||
|
|
8
android-sources/src/StayAwake.java
Normal file
8
android-sources/src/StayAwake.java
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
package com.itsblue;
|
||||||
|
public class StayAwake extends org.qtproject.qt5.android.bindings.QtActivity {
|
||||||
|
@Override
|
||||||
|
public void onCreate(android.os.Bundle savedInstanceState){
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
this.getWindow().addFlags(android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||||
|
}
|
||||||
|
}
|
BIN
graphics/icons/settings.png
Normal file
BIN
graphics/icons/settings.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 324 KiB |
BIN
graphics/icons/user.png
Normal file
BIN
graphics/icons/user.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
BIN
graphics/screenshots/photo_2018-07-17_17-36-30.jpg
Normal file
BIN
graphics/screenshots/photo_2018-07-17_17-36-30.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 42 KiB |
BIN
graphics/screenshots/photo_2018-07-17_17-50-50.jpg
Normal file
BIN
graphics/screenshots/photo_2018-07-17_17-50-50.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 35 KiB |
43
main.cpp
43
main.cpp
|
@ -1,5 +1,41 @@
|
||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
#include <QQmlApplicationEngine>
|
#include <QQmlApplicationEngine>
|
||||||
|
#include <QSqlDatabase>
|
||||||
|
#include <QSqlError>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
#include <QtAndroidExtras>
|
||||||
|
|
||||||
|
#include "sqlstoragemodel.h"
|
||||||
|
#include "sqlprofilemodel.h"
|
||||||
|
|
||||||
|
static void connectToDatabase()
|
||||||
|
{
|
||||||
|
QSqlDatabase database = QSqlDatabase::database();
|
||||||
|
if (!database.isValid()) {
|
||||||
|
database = QSqlDatabase::addDatabase("QSQLITE");
|
||||||
|
if (!database.isValid())
|
||||||
|
qFatal("Cannot add database: %s", qPrintable(database.lastError().text()));
|
||||||
|
}
|
||||||
|
|
||||||
|
const QDir writeDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
|
||||||
|
qDebug() << writeDir;
|
||||||
|
if (!writeDir.mkpath("."))
|
||||||
|
qFatal("Failed to create writable directory at %s", qPrintable(writeDir.absolutePath()));
|
||||||
|
|
||||||
|
// Ensure that we have a writable location on all devices.
|
||||||
|
const QString fileName = writeDir.absolutePath() + "/chat-database.sqlite3";
|
||||||
|
//QFile::remove(fileName);
|
||||||
|
// When using the SQLite driver, open() will create the SQLite database if it doesn't exist.
|
||||||
|
database.setDatabaseName(fileName);
|
||||||
|
if (!database.open()) {
|
||||||
|
QFile::remove(fileName);
|
||||||
|
qFatal("Cannot open database: %s", qPrintable(database.lastError().text()));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
@ -7,6 +43,13 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
QGuiApplication app(argc, argv);
|
QGuiApplication app(argc, argv);
|
||||||
|
|
||||||
|
|
||||||
|
connectToDatabase();
|
||||||
|
|
||||||
|
//setup the sql storage model as a qml model
|
||||||
|
qmlRegisterType<SqlProfileModel>("com.itsblue.speedclimbingstopwatch", 1, 0, "SqlProfileModel");
|
||||||
|
qmlRegisterType<SqlStorageModel>("com.itsblue.speedclimbingstopwatch", 1, 0, "SqlStorageModel");
|
||||||
|
|
||||||
QQmlApplicationEngine engine;
|
QQmlApplicationEngine engine;
|
||||||
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
|
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
|
||||||
if (engine.rootObjects().isEmpty())
|
if (engine.rootObjects().isEmpty())
|
||||||
|
|
461
main.qml
461
main.qml
|
@ -2,6 +2,9 @@ import QtQuick 2.9
|
||||||
import QtMultimedia 5.8
|
import QtMultimedia 5.8
|
||||||
import QtQuick.Window 2.2
|
import QtQuick.Window 2.2
|
||||||
import QtQuick.Controls 2.2
|
import QtQuick.Controls 2.2
|
||||||
|
import QtQuick.Layouts 1.11
|
||||||
|
|
||||||
|
import com.itsblue.speedclimbingstopwatch 1.0
|
||||||
|
|
||||||
Window {
|
Window {
|
||||||
visible: true
|
visible: true
|
||||||
|
@ -20,7 +23,10 @@ Window {
|
||||||
property double stoppedTime: 0
|
property double stoppedTime: 0
|
||||||
property double currTime: new Date().getTime()
|
property double currTime: new Date().getTime()
|
||||||
|
|
||||||
|
state: "IDLE"
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
|
//timer that updates the currTime variable
|
||||||
running: true
|
running: true
|
||||||
repeat: true
|
repeat: true
|
||||||
interval: 1
|
interval: 1
|
||||||
|
@ -29,36 +35,11 @@ Window {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
|
||||||
id: time_container
|
|
||||||
anchors {
|
|
||||||
top: parent.top
|
|
||||||
left: parent.left
|
|
||||||
right: parent.right
|
|
||||||
}
|
|
||||||
height: parent.height * 0.15
|
|
||||||
|
|
||||||
Label {
|
|
||||||
id: time
|
|
||||||
text: "Click start to start"
|
|
||||||
|
|
||||||
anchors.centerIn: parent
|
|
||||||
font.pixelSize: parent.height * 0.3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: parent.width
|
|
||||||
height: 1
|
|
||||||
color: "grey"
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.top: time_container.bottom
|
|
||||||
}
|
|
||||||
|
|
||||||
SoundEffect {
|
SoundEffect {
|
||||||
|
//start sound
|
||||||
id: startSound
|
id: startSound
|
||||||
source: "OFFICAL_IFSC_STARTIGNAL.wav"
|
source: "OFFICAL_IFSC_STARTIGNAL.wav"
|
||||||
|
|
||||||
onPlayingChanged: {
|
onPlayingChanged: {
|
||||||
if(!playing){
|
if(!playing){
|
||||||
root.startTime = new Date().getTime()
|
root.startTime = new Date().getTime()
|
||||||
|
@ -69,18 +50,56 @@ Window {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*------------------------
|
||||||
|
Timer text an upper line
|
||||||
|
------------------------*/
|
||||||
|
Item {
|
||||||
|
id: time_container
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
left: parent.left
|
||||||
|
right: root.landscape() ? startButt.left:parent.right
|
||||||
|
bottom: root.landscape() ? parent.bottom:startButt.top
|
||||||
|
bottomMargin: root.landscape() ? undefined:parent.height * 0.1
|
||||||
|
rightMargin: root.landscape() ? parent.width * 0.05:0
|
||||||
|
}
|
||||||
|
//height: root.landscape() ? undefined:parent.height * 0.15
|
||||||
|
Label {
|
||||||
|
id: time
|
||||||
|
text: "Click start to start"
|
||||||
|
|
||||||
|
anchors.centerIn: parent
|
||||||
|
//font.pixelSize: root.landscape() ? parent.width * 0.1:parent.height * 0.3
|
||||||
|
elide: "ElideRight"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: root.landscape() ? 1:parent.width
|
||||||
|
height: root.landscape() ? parent.height:1
|
||||||
|
color: "grey"
|
||||||
|
anchors.left: root.landscape() ? time_container.right:parent.left
|
||||||
|
anchors.top: root.landscape() ? parent.top:time_container.bottom
|
||||||
|
anchors.bottom: root.landscape() ? parent.bottom:undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------
|
||||||
|
Start button
|
||||||
|
----------------------*/
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: startButt
|
id: startButt
|
||||||
|
|
||||||
property string text: "start"
|
property string text: "start"
|
||||||
|
property int size: root.landscape() ? parent.width * 0.5:parent.height * 0.5
|
||||||
anchors {
|
anchors {
|
||||||
horizontalCenter: parent.horizontalCenter
|
|
||||||
bottom: parent.bottom
|
bottom: parent.bottom
|
||||||
bottomMargin: parent.height * 0.5 - height * 0.5
|
bottomMargin: parent.height * 0.5 - height * 0.5
|
||||||
|
right: parent.right
|
||||||
|
rightMargin: parent.width * 0.5 - width * 0.5
|
||||||
}
|
}
|
||||||
|
|
||||||
height: parent.height - (parent.height * 0.5)
|
height: root.landscape() ? size > parent.height * 0.9 ? parent.height * 0.9:size : size
|
||||||
width: height > parent.width ? parent.width * 0.8:height
|
width: root.landscape() ? size : size > parent.width * 0.9 ? parent.width * 0.9:size
|
||||||
color: "white"
|
color: "white"
|
||||||
border.color: "grey"
|
border.color: "grey"
|
||||||
border.width: 1
|
border.width: 1
|
||||||
|
@ -100,67 +119,352 @@ Window {
|
||||||
onReleased: parent.color = "white"
|
onReleased: parent.color = "white"
|
||||||
onClicked: {
|
onClicked: {
|
||||||
switch(root.state) {
|
switch(root.state) {
|
||||||
case "":
|
case "":
|
||||||
root.state = "IDLE"
|
root.state = "IDLE"
|
||||||
case "IDLE":
|
case "IDLE":
|
||||||
root.state = "STARTING"
|
root.state = "STARTING"
|
||||||
startSound.play()
|
startSound.play()
|
||||||
break
|
break
|
||||||
case "RUNNING":
|
case "RUNNING":
|
||||||
root.stoppedTime = new Date().getTime() - root.startTime
|
root.stoppedTime = new Date().getTime() - root.startTime
|
||||||
time.text = ( root.stoppedTime / 1000 ).toFixed(3) + " sec"
|
time.text = ( root.stoppedTime / 1000 ).toFixed(3) + " sec"
|
||||||
root.state = "STOPPED"
|
root.state = "STOPPED"
|
||||||
break
|
break
|
||||||
case "STOPPED":
|
case "STOPPED":
|
||||||
root.state = "IDLE"
|
root.state = "IDLE"
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
states: [
|
|
||||||
State {
|
|
||||||
name: "IDLE"
|
|
||||||
//state for the start page
|
|
||||||
PropertyChanges { target: time; text: "Click start to start"; font.pixelSize: parent.height * 0.3 }
|
|
||||||
PropertyChanges { target: time_container; height: parent.height * 0.15 }
|
|
||||||
PropertyChanges { target: startButt; enabled: true; text: "start"; height: parent.height - (parent.height * 0.5); anchors.bottomMargin: parent.height * 0.5 - startButt.height * 0.5 }
|
|
||||||
},
|
|
||||||
State {
|
|
||||||
name: "STARTING"
|
|
||||||
//state for the start sequence
|
|
||||||
PropertyChanges { target: startButt; enabled: false; text: "starting..." }
|
|
||||||
PropertyChanges { target: time; text: "0.000 sec" }
|
|
||||||
},
|
|
||||||
State {
|
|
||||||
name: "RUNNING"
|
|
||||||
//state when the timer is running
|
|
||||||
PropertyChanges { target: time; text: ( ( root.currTime - root.startTime ) / 1000 ).toFixed(3) + " sec" }
|
|
||||||
PropertyChanges { target: startButt; enabled: true; text: "stop" }
|
|
||||||
|
|
||||||
},
|
/*----------------------
|
||||||
|
Cancel button
|
||||||
|
----------------------*/
|
||||||
|
Rectangle {
|
||||||
|
id: cancelButt
|
||||||
|
|
||||||
State {
|
property string text: "cancel"
|
||||||
name: "STOPPED"
|
anchors {
|
||||||
//state when the meassuring is over
|
right: startButt.right
|
||||||
PropertyChanges { target: time; text: ( root.stoppedTime / 1000 ).toFixed(3) + " sec"; font.pixelSize: parent.height * 0.1 }
|
bottom: startButt.bottom
|
||||||
PropertyChanges { target: startButt; enabled: true; text: "reset"; height: parent.height - (parent.height * 0.8); anchors.bottomMargin: parent.height * 0.2 - startButt.height * 0.5 }
|
}
|
||||||
PropertyChanges { target: time_container; height: parent.height * 0.8 }
|
height: startButt.height * 0.3
|
||||||
|
scale: 0
|
||||||
|
width: height
|
||||||
|
color: "white"
|
||||||
|
border.color: "grey"
|
||||||
|
border.width: 1
|
||||||
|
radius: width / 2
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: cancelButt_text
|
||||||
|
text: parent.text
|
||||||
|
anchors.centerIn: parent
|
||||||
|
font.pixelSize: parent.height * 0.16
|
||||||
|
font.family: "Helvetica"
|
||||||
|
}
|
||||||
|
MouseArea {
|
||||||
|
enabled: startSound.playing
|
||||||
|
anchors.fill: parent
|
||||||
|
onPressed: parent.color = "lightgrey"
|
||||||
|
onReleased: parent.color = "white"
|
||||||
|
onClicked: {
|
||||||
|
startSound.stop()
|
||||||
|
root.stoppedTime = 0
|
||||||
|
time.text = "false start"
|
||||||
|
root.state = "STOPPED"
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
|
|
||||||
|
Behavior on scale {
|
||||||
|
PropertyAnimation {
|
||||||
|
duration: 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------
|
||||||
|
lower line and menu
|
||||||
|
-------------------*/
|
||||||
|
Rectangle {
|
||||||
|
width: root.landscape() ? 1:parent.width
|
||||||
|
height: root.landscape() ? parent.height:1
|
||||||
|
color: "grey"
|
||||||
|
anchors.right: root.landscape() ? menu_container.left:parent.right
|
||||||
|
anchors.bottom: root.landscape() ? parent.bottom:menu_container.top
|
||||||
|
anchors.top: root.landscape() ? parent.top:undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: menu_container
|
||||||
|
anchors {
|
||||||
|
bottom: parent.bottom
|
||||||
|
right: parent.right
|
||||||
|
left: root.landscape() ? startButt.right:parent.left
|
||||||
|
top: root.landscape() ? parent.top:startButt.bottom
|
||||||
|
topMargin: root.landscape() ? undefined:parent.height * 0.1
|
||||||
|
leftMargin: root.landscape() ? parent.width * 0.05:0
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: settingsButt
|
||||||
|
|
||||||
|
property string text: "cancel"
|
||||||
|
anchors {
|
||||||
|
//center
|
||||||
|
verticalCenter: root.landscape() ? undefined:parent.verticalCenter
|
||||||
|
horizontalCenter: root.landscape() ? parent.horizontalCenter:undefined
|
||||||
|
//set anchors
|
||||||
|
left: root.landscape() ? undefined:parent.left
|
||||||
|
top: root.landscape() ? parent.top:undefined
|
||||||
|
//align in landscape mode
|
||||||
|
topMargin: root.landscape() ? (parent.height - (height * 2)) / 3:undefined
|
||||||
|
//align in portrait mode
|
||||||
|
leftMargin: root.landscape() ? undefined:(parent.width - width * 2) / 3
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
height: root.landscape() ? parent.width * 0.7:parent.height * 0.7
|
||||||
|
width: height
|
||||||
|
color: "white"
|
||||||
|
border.color: "grey"
|
||||||
|
border.width: 1
|
||||||
|
radius: width / 2
|
||||||
|
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: settungsButt_Image
|
||||||
|
source: "qrc:/graphics/icons/settings.png"
|
||||||
|
anchors.centerIn: parent
|
||||||
|
height: parent.height * 0.7
|
||||||
|
width: parent.width * 0.7
|
||||||
|
mipmap: true
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
enabled: root.state === "IDLE"
|
||||||
|
anchors.fill: parent
|
||||||
|
onPressed: parent.color = "lightgrey"
|
||||||
|
onReleased: parent.color = "white"
|
||||||
|
onClicked: {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on scale {
|
||||||
|
PropertyAnimation {
|
||||||
|
duration: 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Rectangle {
|
||||||
|
id: profilesButt
|
||||||
|
|
||||||
|
property string text: "cancel"
|
||||||
|
anchors {
|
||||||
|
verticalCenter: root.landscape() ? undefined:parent.verticalCenter
|
||||||
|
horizontalCenter: root.landscape() ? parent.horizontalCenter:undefined
|
||||||
|
left: root.landscape() ? undefined:settingsButt.right
|
||||||
|
top: root.landscape() ? settingsButt.bottom:undefined
|
||||||
|
topMargin: root.landscape() ? (parent.height - (height * 2)) / 3:undefined
|
||||||
|
leftMargin: root.landscape() ? undefined:(parent.width - width * 2) / 3
|
||||||
|
}
|
||||||
|
height: root.landscape() ? parent.width * 0.7:parent.height * 0.7
|
||||||
|
|
||||||
|
width: height
|
||||||
|
color: "white"
|
||||||
|
border.color: "grey"
|
||||||
|
border.width: 1
|
||||||
|
radius: width / 2
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: profilesButt_Image
|
||||||
|
source: "qrc:/graphics/icons/user.png"
|
||||||
|
anchors.centerIn: parent
|
||||||
|
height: parent.height * 0.5
|
||||||
|
width: parent.width * 0.5
|
||||||
|
mipmap: true
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
enabled: root.state === "IDLE"
|
||||||
|
anchors.fill: parent
|
||||||
|
onPressed: parent.color = "lightgrey"
|
||||||
|
onReleased: parent.color = "white"
|
||||||
|
onClicked: {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on scale {
|
||||||
|
PropertyAnimation {
|
||||||
|
duration: 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
// 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
|
||||||
|
----------------------*/
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
name: "IDLE"
|
||||||
|
//state for the start page
|
||||||
|
PropertyChanges { target: time; text: "Click start to start"; font.pixelSize: root.landscape() ? parent.width * 0.1:parent.height * 0.3; scale: 1 }
|
||||||
|
PropertyChanges {
|
||||||
|
target: time_container;
|
||||||
|
anchors.bottomMargin: root.landscape() ? undefined:parent.height * 0.1;
|
||||||
|
anchors.rightMargin: root.landscape() ? parent.height * 0.05:0
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: startButt;
|
||||||
|
enabled: true; text: "start";
|
||||||
|
size: root.landscape() ? parent.width * 0.5:parent.height * 0.5
|
||||||
|
anchors.bottomMargin: parent.height * 0.5 - startButt.height * 0.5
|
||||||
|
anchors.rightMargin: parent.width * 0.5 - startButt.width * 0.5
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "STARTING"
|
||||||
|
//state for the start sequence
|
||||||
|
PropertyChanges { target: startButt; enabled: false; text: "starting...";
|
||||||
|
anchors.rightMargin: root.landscape() ? parent.width * 0.05:none //put the button more to the right to hide the menu (only in landscape mode)
|
||||||
|
anchors.bottomMargin: root.landscape() ? none:parent.height * 0.1 //put the button lower to hide the menu (only in portrait mode)
|
||||||
|
}
|
||||||
|
PropertyChanges { target: time; text: "0.000 sec"; font.pixelSize: root.landscape() ? parent.width * 0.2:parent.height * 0.3; scale: 1 }
|
||||||
|
PropertyChanges { target: cancelButt; scale: 1}
|
||||||
|
PropertyChanges { target: menu_container; }
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "RUNNING"
|
||||||
|
//state when the timer is running
|
||||||
|
PropertyChanges { target: time; text: ( ( root.currTime - root.startTime ) / 1000 ).toFixed(3) + " sec"; font.pixelSize: root.landscape() ? parent.width * 0.2:parent.height * 0.3; scale: 1 }
|
||||||
|
PropertyChanges { target: startButt; enabled: true;
|
||||||
|
text: "stop"
|
||||||
|
anchors.rightMargin: root.landscape() ? parent.width * 0.05:none //put the button more to the right to hide the menu (only in landscape mode)
|
||||||
|
anchors.bottomMargin: root.landscape() ? none:parent.height * 0.1 //put the button lower to hide the menu (only in portrait mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
State {
|
||||||
|
name: "STOPPED"
|
||||||
|
//state when the meassuring is over
|
||||||
|
PropertyChanges {
|
||||||
|
target: time; text: root.stoppedTime > 0 ? ( root.stoppedTime / 1000 ).toFixed(3) + " sec":"false start";
|
||||||
|
font.pixelSize: root.landscape() ? parent.width * 0.15:parent.height * 0.1;
|
||||||
|
scale: 1
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: startButt;
|
||||||
|
enabled: true; text: "reset";
|
||||||
|
size: root.landscape() ? parent.height * 0.35:parent.height * 0.2;
|
||||||
|
anchors.bottomMargin: root.landscape() ? parent.height * 0.5 - startButt.height * 0.5:parent.height * 0.2 - startButt.height * 0.5
|
||||||
|
anchors.rightMargin: root.landscape() ? parent.height * 0.2 - startButt.height * 0.5:parent.width * 0.5 - startButt.width * 0.5
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: time_container;
|
||||||
|
anchors.rightMargin: root.landscape() ? 0-startButt.width/2:undefined
|
||||||
|
anchors.bottomMargin: root.landscape() ? undefined:0-startButt.height/2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
/*----------------------
|
||||||
|
Timer animations
|
||||||
|
----------------------*/
|
||||||
transitions: [
|
transitions: [
|
||||||
Transition {
|
Transition {
|
||||||
NumberAnimation { properties: "height,width,bottomMargin,font.pixelSize"; easing.type: Easing.InOutQuad; duration: 700 }
|
NumberAnimation { properties: "size,rightMargin,height,width,bottomMargin,font.pixelSize"; easing.type: Easing.InOutQuad; duration: 700 }
|
||||||
FadeAnimation { target: time; fadeDuration_in: 0; fadeDuration_out: 0 }
|
|
||||||
},
|
},
|
||||||
Transition {
|
Transition {
|
||||||
to: "STOPPED"
|
to: "STOPPED"
|
||||||
NumberAnimation { properties: "height,width,bottomMargin,font.pixelSize"; easing.type: Easing.InOutQuad; duration: 700 }
|
NumberAnimation { properties: "size,rightMargin,height,width,bottomMargin,font.pixelSize"; easing.type: Easing.InOutQuad; duration: 700 }
|
||||||
},
|
},
|
||||||
Transition {
|
Transition {
|
||||||
to: "IDLE"
|
to: "IDLE"
|
||||||
NumberAnimation { properties: "height,width,bottomMargin,font.pixelSize"; easing.type: Easing.InOutQuad; duration: 700 }
|
NumberAnimation { properties: "size,rightMargin,height,width,bottomMargin,font.pixelSize"; easing.type: Easing.InOutQuad; duration: 700 }
|
||||||
FadeAnimation { target: time; fadeDuration_out: 1000; fadeDuration_in: 0}
|
FadeAnimation { target: time; fadeDuration_out: 1000; fadeDuration_in: 0}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -169,5 +473,12 @@ Window {
|
||||||
//disable transitions for the RUNNING state
|
//disable transitions for the RUNNING state
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
/*----------------------
|
||||||
|
Timer functions
|
||||||
|
----------------------*/
|
||||||
|
function landscape(){
|
||||||
|
return(root.height < root.width)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
<RCC>
|
<RCC>
|
||||||
<qresource prefix="/">
|
<qresource prefix="/">
|
||||||
<file>OFFICAL_IFSC_STARTIGNAL.wav</file>
|
<file>OFFICAL_IFSC_STARTIGNAL.wav</file>
|
||||||
|
<file>graphics/icons/settings.png</file>
|
||||||
|
<file>graphics/icons/user.png</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
QT += quick
|
QT += quick sql androidextras
|
||||||
CONFIG += c++11
|
CONFIG += c++11
|
||||||
|
|
||||||
# The following define makes your compiler emit warnings if you use
|
# The following define makes your compiler emit warnings if you use
|
||||||
|
@ -15,7 +15,9 @@ DEFINES += QT_DEPRECATED_WARNINGS
|
||||||
TARGET = speedclimbing_stw
|
TARGET = speedclimbing_stw
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
main.cpp
|
main.cpp \
|
||||||
|
sqlstoragemodel.cpp \
|
||||||
|
sqlprofilemodel.cpp
|
||||||
|
|
||||||
RESOURCES += qml.qrc \
|
RESOURCES += qml.qrc \
|
||||||
shared.qrc
|
shared.qrc
|
||||||
|
@ -35,8 +37,13 @@ else: unix:!android: target.path = /opt/$${TARGET}/bin
|
||||||
!isEmpty(target.path): INSTALLS += target
|
!isEmpty(target.path): INSTALLS += target
|
||||||
|
|
||||||
DISTFILES += \
|
DISTFILES += \
|
||||||
android-sources/AndroidManifest.xml
|
android-sources/AndroidManifest.xml \
|
||||||
|
android-sources/src/StayAwake.java
|
||||||
|
|
||||||
android {
|
android {
|
||||||
ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android-sources
|
ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android-sources
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
sqlstoragemodel.h \
|
||||||
|
sqlprofilemodel.h
|
||||||
|
|
56
sqlprofilemodel.cpp
Normal file
56
sqlprofilemodel.cpp
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
#include "sqlprofilemodel.h"
|
||||||
|
|
||||||
|
static void createTable()
|
||||||
|
{
|
||||||
|
if (QSqlDatabase::database().tables().contains(QStringLiteral("Contacts"))) {
|
||||||
|
// The table already exists; we don't need to do anything.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSqlQuery query;
|
||||||
|
|
||||||
|
//creat eth etable to store the profiles
|
||||||
|
if (!query.exec(
|
||||||
|
"CREATE TABLE IF NOT EXISTS `profiles` ( "
|
||||||
|
" `id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,"
|
||||||
|
" `name` TEXT NOT NULL "
|
||||||
|
" );")) {
|
||||||
|
qFatal("Failed to query database: %s", qPrintable(query.lastError().text()));
|
||||||
|
}
|
||||||
|
|
||||||
|
//create the table to store the times
|
||||||
|
if (!query.exec(
|
||||||
|
"CREATE TABLE IF NOT EXISTS `times` ("
|
||||||
|
" `id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,"
|
||||||
|
" `profileid` INTEGER NOT NULL,"
|
||||||
|
" `time` INTEGER NOT NULL"
|
||||||
|
" );")) {
|
||||||
|
qFatal("Failed to query database: %s", qPrintable(query.lastError().text()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SqlProfileModel::SqlProfileModel(QObject *parent) : QSqlTableModel(parent)
|
||||||
|
{
|
||||||
|
qDebug("ProfileModel constructor");
|
||||||
|
createTable();
|
||||||
|
setTable("profiles");
|
||||||
|
select();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant SqlProfileModel::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<int, QByteArray> SqlProfileModel::roleNames() const
|
||||||
|
{
|
||||||
|
QHash<int, QByteArray> names;
|
||||||
|
names[Qt::UserRole + 0] = "id";
|
||||||
|
names[Qt::UserRole + 1] = "name";
|
||||||
|
|
||||||
|
return names;
|
||||||
|
}
|
25
sqlprofilemodel.h
Normal file
25
sqlprofilemodel.h
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
#ifndef SQLPROFILEMODEL_H
|
||||||
|
#define SQLPROFILEMODEL_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QSqlError>
|
||||||
|
#include <QSqlQuery>
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QSqlRecord>
|
||||||
|
#include <QSqlTableModel>
|
||||||
|
|
||||||
|
class SqlProfileModel : public QSqlTableModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit SqlProfileModel(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE;
|
||||||
|
QHash<int, QByteArray> roleNames() const Q_DECL_OVERRIDE;
|
||||||
|
signals:
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SQLPROFILEMODEL_H
|
6
sqlstoragemodel.cpp
Normal file
6
sqlstoragemodel.cpp
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
#include "sqlstoragemodel.h"
|
||||||
|
|
||||||
|
SqlStorageModel::SqlStorageModel(QObject *parent) : QObject(parent)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
17
sqlstoragemodel.h
Normal file
17
sqlstoragemodel.h
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#ifndef SQLSTORAGEMODEL_H
|
||||||
|
#define SQLSTORAGEMODEL_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
class SqlStorageModel : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit SqlStorageModel(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SQLSTORAGEMODEL_H
|
Reference in a new issue