This repository has been archived on 2024-06-03. You can view files and clone it, but cannot push or open issues or pull requests.
app/main.qml
Dorian Zedler 90adc6bc0a added start sounds
completed start sequence
2018-07-28 21:17:41 +02:00

655 lines
23 KiB
QML

import QtQuick 2.9
import QtMultimedia 5.8
import QtQuick.Window 2.2
import QtQuick.Controls 2.2
//import QtQuick.Layouts 1.11
import com.itsblue.speedclimbingstopwatch 1.0
Window {
visible: true
width: 540
height: 960
title: qsTr("Speedclimbing stw")
property date currentTime: new Date()
property int millis: 0
Page {
id:root
anchors.fill: parent
property double startTime: 0
property double stoppedTime: 0
property double currTime
property double buzzer_offset
property double last_button_pressed
state: "IDLE"
Timer {
//timer that updates the currTime variable
running: true
repeat: true
interval: 1
onTriggered: {
root.currTime = new Date().getTime()
}
}
Timer {
//timer that refreshes the connection state to the buzzer
running: false
repeat: true
interval: 500
onTriggered: {
console.log(_cppBuzzerConn.calcoffset())
}
}
Timer {
id: running_refresh_timer
running: root.state === "RUNNING"
repeat: false
interval: 1
onTriggered: {
if(_cppBuzzerConn.buzzer_triggered()){
//the buzzer was pushed
root.buzzer_offset = _cppBuzzerConn.get("offset")
root.last_button_pressed = _cppBuzzerConn.get("lastpressed")
root.stoppedTime = (root.last_button_pressed + root.buzzer_offset) - root.startTime
root.state = "STOPPED"
//time.text = ( root.stoppedTime / 1000 ).toFixed(3) + " sec"
console.log("STOPPED: "+root.stoppedTime)
}
if(root.state === "RUNNING"){
running_refresh_timer.start()
}
}
}
Timer {
id: next_actionTimer
property string action
property double started_at
running: false
repeat: false
onRunningChanged: {
if(!running){
started_at = 0
if(action == "NONE"){
time.text = "0.000 sec"
}
return
}
if(action === "at_marks"){
started_at = new Date().getTime()
time.text = "at your\nmarks"
}
else if(action === "ready"){
started_at = new Date().getTime()
time.text = "ready"
}
}
onTriggered: {
if(action === "at_marks"){
at_marksSound.play()
}
else if(action === "ready"){
action = "NONE"
readySound.play()
}
}
}
SoundEffect {
id: at_marksSound
source: "qrc:/sounds/at_marks_1.wav"
onPlayingChanged: {
if(!playing){
if(_cppAppSettings.loadSetting("ready_en") === "true"){
next_actionTimer.action = "ready"
next_actionTimer.interval = _cppAppSettings.loadSetting("ready_delay")
next_actionTimer.start()
//readySound.play()
}
else{
startSound.play()
}
}
}
}
SoundEffect {
id: readySound
source: "qrc:/sounds/ready_1.wav"
onPlayingChanged: {
if(!playing){
startSound.play()
}
}
}
SoundEffect {
//start sound
id: startSound
source: "OFFICAL_IFSC_STARTIGNAL.wav"
onPlayingChanged: {
if(!playing){
root.startTime = _cppBuzzerConn.get("currtime")
_cppBuzzerConn.start()
root.currTime = _cppBuzzerConn.get("currtime")
time.text = ( ( root.currTime - root.startTime ) / 1000 ).toFixed(3) + " sec"
root.state = "RUNNING"
}
}
}
/*------------------------
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
----------------------*/
Button {
id : startButt
text: "start"
property int size: root.landscape() ? parent.width * 0.5:parent.height * 0.5
anchors {
bottom: parent.bottom
bottomMargin: root.height * 0.5 - height * 0.5
right: parent.right
rightMargin: root.width * 0.5 - width * 0.5
}
contentItem: Text {
//make text disappear
}
height: root.landscape() ? size > parent.height * 0.9 ? parent.height * 0.9:size : size
width: root.landscape() ? size : size > parent.width * 0.9 ? parent.width * 0.9:size
onPressedChanged: {
if(pressed){
background.color = "lightgrey"
}
else {
background.color = "white"
}
}
background: Rectangle {
color: "white"
border.color: "grey"
border.width: 1
radius: width / 2
Label {
id: startButt_text
text: startButt.text
anchors.centerIn: parent
font.pixelSize: parent.height * 0.16
font.family: "Helvetica"
}
}
onClicked: {
switch(root.state) {
case "":
root.state = "IDLE"
case "IDLE":
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.start()
return
}
if(_cppAppSettings.loadSetting("ready_en") === "true"){
next_actionTimer.action = "ready"
next_actionTimer.interval = _cppAppSettings.loadSetting("ready_delay")
next_actionTimer.start()
return
}
startSound.play()
break
case "RUNNING":
root.stoppedTime = new Date().getTime() - root.startTime
time.text = ( root.stoppedTime / 1000 ).toFixed(3) + " sec"
root.state = "STOPPED"
break
case "STOPPED":
root.state = "IDLE"
break
}
}
}
ProgressCircle {
id: prog
anchors.fill: startButt
opacity: next_actionTimer.started_at > 0 ? 1:0
lineWidth: 5
arcBegin: 0
arcEnd: 360 * (( next_actionTimer.interval - ( new Date().getTime() - next_actionTimer.started_at ) ) / next_actionTimer.interval)
colorCircle: "grey"
animationDuration: 0
Timer {
id: prog_refresh
running: parent.opacity === 1
interval: 1
repeat: true
onTriggered: {
prog.arcEnd = 360 * (( next_actionTimer.interval - ( new Date().getTime() - next_actionTimer.started_at ) ) / next_actionTimer.interval)
}
}
}
/*----------------------
Cancel button
----------------------*/
RoundButton {
id: cancelButt
text: "cancel"
anchors {
right: startButt.right
bottom: startButt.bottom
}
contentItem: Text {
//make text disappear
}
height: startButt.height * 0.3
scale: 0
width: height
enabled: root.state === "STARTING"
onPressedChanged: {
if(pressed){
background.color = "lightgrey"
}
else {
background.color = "white"
}
}
onClicked: {
next_actionTimer.stop()
at_marksSound.stop()
readySound.stop()
startSound.stop()
root.stoppedTime = 0
time.text = "false start"
root.state = "STOPPED"
}
Behavior on scale {
PropertyAnimation {
duration: 200
}
}
background: Rectangle {
color: "white"
border.color: "grey"
border.width: 1
radius: width / 2
Label {
id: cancelButt_text
text: cancelButt.text
anchors.centerIn: parent
font.pixelSize: parent.height * 0.16
font.family: "Helvetica"
}
}
}
/*------
Popups
------*/
SettingsDialog{
id:settingsDialog
}
/*-------------------
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
}
RoundButton {
id: settingsButt
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
onPressedChanged: {
if(pressed){
background.color = "lightgrey"
}
else {
background.color = "white"
}
}
onClicked: {
settingsDialog.open()
}
background: Rectangle {
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
}
}
}
RoundButton {
id: profilesButt
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
onPressedChanged: {
if(pressed){
background.color = "lightgrey"
}
else {
background.color = "white"
}
}
onClicked: {
}
background: Rectangle {
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
}
}
}
}
/*
// 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: [
Transition {
NumberAnimation { properties: "size,rightMargin,height,width,bottomMargin,font.pixelSize"; easing.type: Easing.InOutQuad; duration: 700 }
},
Transition {
to: "STOPPED"
NumberAnimation { properties: "size,rightMargin,height,width,bottomMargin,font.pixelSize"; easing.type: Easing.InOutQuad; duration: 700 }
},
Transition {
to: "IDLE"
NumberAnimation { properties: "size,rightMargin,height,width,bottomMargin,font.pixelSize"; easing.type: Easing.InOutQuad; duration: 700 }
FadeAnimation { target: time; fadeDuration_out: 1000; fadeDuration_in: 0}
},
Transition {
to: "RUNNING"
//disable transitions for the RUNNING state
}
]
/*----------------------
Timer functions
----------------------*/
function landscape(){
return(root.height < root.width)
}
}
}