Speedometer
-
@Darq - without seeing what code you have so far, I don't know how to answer your question. Please post some working code, or what you have so far. It is much easier to see and have ideas when one can see some code.
EDIT: As I am already using CircularSlider from arunpkqt in several projects, I can easily adapt my code with your code and possibly work out a way to get what you want.
@Markkyboy
HmiImage {
id: pointer1
anchors.centerIn: parent
source: "icons/abc.png"
rotation: 2.25*XXX+225
}XXX is my measured value
225 is an angle where the value is 0
2.25 is a degrees conversion, so for example, my value indicates 1, which corresponds to 2.25 degrees -
@Markkyboy
HmiImage {
id: pointer1
anchors.centerIn: parent
source: "icons/abc.png"
rotation: 2.25*XXX+225
}XXX is my measured value
225 is an angle where the value is 0
2.25 is a degrees conversion, so for example, my value indicates 1, which corresponds to 2.25 degrees -
@Markkyboy There is almost no more code there. There is only one more HmiImage defining the appearance of the shield (it loads the shields, i.e. a png file). In this code snippet there is only the "source" command which specifies the location of the png file. The png file has a scale drawn on it. The hint is called by the code above, which specifies its position (e.g. point 0 is at 225 degrees, so in the png file 0 is drawn at 225 degrees).
-
@Markkyboy There is almost no more code there. There is only one more HmiImage defining the appearance of the shield (it loads the shields, i.e. a png file). In this code snippet there is only the "source" command which specifies the location of the png file. The png file has a scale drawn on it. The hint is called by the code above, which specifies its position (e.g. point 0 is at 225 degrees, so in the png file 0 is drawn at 225 degrees).
@Darq - sorry, I just don't understand what you're trying to do. I can take plenty of wild guesses but I don't have the time to do so.
Making a functional speedometer requires a lot more code in my opinion. You could at the very least show an image/screenshot of what you have, otherwise I'm in the dark. At the very least, a mock up image of what you're wanting might be helpful.
What kind of speedo only functions when hitting 50?, what is the idea of this?, 50 what?, kmh?, mph?, kts?.....?
What is "icons/abc.png"?, what does it look like?
What is "hmiImage", where is that code?
Are you taking ANY cues from arunpkqt's application?
Seriously now, what you write here gives me very little to go on. I'm not sure I can help you.
-
Rather than leave you completely in the dark, Darq, here's some full code to make a working speedometer. Perhaps from this you can make it do what you want/require.
import QtQuick import QtQuick.Controls Item { id: control property int trackWidth: 20 property int progressWidth: 20 property color trackColor: "#505050" property color progressColor: "#3a4ec4" property bool hideTrack: false property bool hideProgress: false property color handleColor: "#fefefe" property int handleWidth: 80 property int handleHeight: 80 property int handleRadius: 40 property int handleVerticalOffset: 0 property real startAngle: 0 property real endAngle: 360 property real minValue: 0.0 property real maxValue: 1.0 property real value: 0.0 readonly property alias angle: internal.angle property real stepSize: 0 property bool snap: false property Component handle: null readonly property alias pressed: trackMouse.pressed property bool interactive: true property alias cursorShape: trackMouse.cursorShape implicitWidth: 250 implicitHeight: 250 Binding { target: control property: "value" value: control.snap ? internal.snappedValue : internal.mapFromValue(startAngle, endAngle, minValue, maxValue, internal.angleProxy) when: internal.setUpdatedValue } QtObject { id: internal property point centerPt: Qt.point(control.width / 2, control.height / 2) property real baseRadius: Math.min(control.width, control.height) / 2 - Math.max(control.trackWidth, control.progressWidth) / 2 property real actualSpanAngle: control.endAngle - control.startAngle property color transparentColor: "transparent" property color trackColor: control.trackColor property bool setUpdatedValue: false property real angleProxy: control.startAngle property real snappedValue: 0.0 readonly property real angle: internal.mapFromValue(control.minValue, control.maxValue, control.startAngle, control.endAngle, control.value) function mapFromValue(inMin, inMax, outMin, outMax, inValue) { return (inValue - inMin) * (outMax - outMin) / (inMax - inMin) + outMin; } function updateAngle(angleVal) { if (angleVal < 0) angleVal += 360; if ((angleVal >= control.startAngle) && (angleVal <= control.endAngle)) { internal.setUpdatedValue = true; internal.angleProxy = Qt.binding(function() { return angleVal; }); if(control.snap) { var mappedValue = internal.mapFromValue(startAngle, endAngle, minValue, maxValue, internal.angleProxy) var range = control.maxValue - control.minValue var effectiveStep = 2 var actualVal = control.stepSize * Math.round(mappedValue / control.stepSize) internal.snappedValue = actualVal } internal.setUpdatedValue = false; } } } MouseArea { anchors.fill: parent enabled: control.interactive onClicked: { var outerRadius = Math.min(control.width, control.height)/ 2 var innerRadius = outerRadius - Math.max(control.trackWidth, 20); var clickedDistance = (mouseX - internal.centerPt.x) * (mouseX - internal.centerPt.x) + (mouseY - internal.centerPt.y) * (mouseY - internal.centerPt.y); var innerRadius2 = (innerRadius * innerRadius); var outerRadius2 = (outerRadius * outerRadius); var isOutOfInnerRadius = clickedDistance > innerRadius2; var inInSideOuterRadius = clickedDistance <= outerRadius2; if (inInSideOuterRadius && isOutOfInnerRadius) { var angleDeg = Math.atan2(mouseY - internal.centerPt.y, mouseX - internal.centerPt.x) * 180 / Math.PI + 90; internal.updateAngle(angleDeg); } } } // Handle Item Item { id: handleItem visible: control.interactive x: control.width / 2 - width / 2 y: control.height / 2 - height / 2 // make sure that the slider handle is always on top as we can set custom track or progress items z: 2 width: control.handleWidth height: control.handleHeight antialiasing: true transform: [ Translate { y: -(Math.min(control.width, control.height) / 2) + Math.max(control.trackWidth, control.progressWidth) / 2 + control.handleVerticalOffset }, Rotation { angle: control.angle origin.x: handleItem.width / 2 origin.y: handleItem.height / 2 } ] MouseArea { id: trackMouse enabled: control.interactive function getVal() { var handlePoint = mapToItem(control, trackMouse.mouseX, trackMouse.mouseY); // angle in degrees var angleDeg = Math.atan2(handlePoint.y - internal.centerPt.y, handlePoint.x - internal.centerPt.x) * 180 / Math.PI + 90; internal.updateAngle(angleDeg); } anchors.fill: parent onPositionChanged: getVal() onClicked: getVal() cursorShape: Qt.ArrowCursor } Loader { id: handleLoader sourceComponent: control.handle ? handle : handleComponent } } /// Default handle component Component { id: handleComponent Rectangle { width: control.handleWidth height: control.handleHeight color: control.handleColor radius: control.handleRadius antialiasing: true } } }
import QtQuick import QtQuick.Controls import "../components" Page { id: darqSpeedo allowedOrientations: Orientation.All // this is specific to SailfishOS and can likely be omitted. // imported from "../components" CircularSlider { id: speedometer interactive: false anchors.centerIn: parent width: 1000 height: width value: 0 startAngle: -108 endAngle: 108 minValue: 0 maxValue: 180 anchors { verticalCenterOffset: 200 verticalCenter: parent.verticalCenter horizontalCenter: parent.horizontalCenter } // Tickmarks around gauge Repeater { model: 37 Rectangle { id: ones width: 6 height: index % 4 === 0 ? 50 : 25 color: index % 4 === 0 ? "white" : "silver" property real angle: index * 6 transform: [ Translate { x: parent.width / 2 - width / 2; y: -155 }, Rotation { origin.x: parent.width / 2 origin.y: parent.height / 2 angle: ones.angle + parent.startAngle } ] } } // Numbers around gauge Repeater { model: 10 Label { id: digits text: index * 20 color: "white" font { bold: true; pixelSize: 48 } property real angle: index * 24 transform: [ Translate { x: parent.width / 2 - width / 2 y: -75 // distance from tickmarks }, Rotation { origin.x: parent.width / 2 origin.y: parent.height / 2 angle: digits.angle + parent.startAngle } ] // keeps numbers horizontal rotation: -digits.angle - parent.startAngle } } // Needle hub Rectangle { anchors.centerIn: speedometer width: 100 height: width radius: width/2 color: "#a0545454" } // Needle/pointer Repeater { model: 1 Rectangle { id: needle width: 20 height: 450 color: "orange" radius: 20 transform: [ Translate { x: speedometer.width / 2 - width / 2 y: 50 // needle distance from centre }, Rotation { origin.x: speedometer.width / 2 origin.y: speedometer.height / 2 angle: speedometer.angle } ] } } // Animation for demo purposes SequentialAnimation on value { id: animation loops: Animation.Infinite running: true NumberAnimation { from: 0; to: 180; duration: 2000 } NumberAnimation { from: 180; to: 0; duration: 2000 } } } }
NOTE: I use
Page
, as my apps are built for my mobile phone using SailfishOS. You don't have to usePage
, you can likely swap it forItem
instead. -
Rather than leave you completely in the dark, Darq, here's some full code to make a working speedometer. Perhaps from this you can make it do what you want/require.
import QtQuick import QtQuick.Controls Item { id: control property int trackWidth: 20 property int progressWidth: 20 property color trackColor: "#505050" property color progressColor: "#3a4ec4" property bool hideTrack: false property bool hideProgress: false property color handleColor: "#fefefe" property int handleWidth: 80 property int handleHeight: 80 property int handleRadius: 40 property int handleVerticalOffset: 0 property real startAngle: 0 property real endAngle: 360 property real minValue: 0.0 property real maxValue: 1.0 property real value: 0.0 readonly property alias angle: internal.angle property real stepSize: 0 property bool snap: false property Component handle: null readonly property alias pressed: trackMouse.pressed property bool interactive: true property alias cursorShape: trackMouse.cursorShape implicitWidth: 250 implicitHeight: 250 Binding { target: control property: "value" value: control.snap ? internal.snappedValue : internal.mapFromValue(startAngle, endAngle, minValue, maxValue, internal.angleProxy) when: internal.setUpdatedValue } QtObject { id: internal property point centerPt: Qt.point(control.width / 2, control.height / 2) property real baseRadius: Math.min(control.width, control.height) / 2 - Math.max(control.trackWidth, control.progressWidth) / 2 property real actualSpanAngle: control.endAngle - control.startAngle property color transparentColor: "transparent" property color trackColor: control.trackColor property bool setUpdatedValue: false property real angleProxy: control.startAngle property real snappedValue: 0.0 readonly property real angle: internal.mapFromValue(control.minValue, control.maxValue, control.startAngle, control.endAngle, control.value) function mapFromValue(inMin, inMax, outMin, outMax, inValue) { return (inValue - inMin) * (outMax - outMin) / (inMax - inMin) + outMin; } function updateAngle(angleVal) { if (angleVal < 0) angleVal += 360; if ((angleVal >= control.startAngle) && (angleVal <= control.endAngle)) { internal.setUpdatedValue = true; internal.angleProxy = Qt.binding(function() { return angleVal; }); if(control.snap) { var mappedValue = internal.mapFromValue(startAngle, endAngle, minValue, maxValue, internal.angleProxy) var range = control.maxValue - control.minValue var effectiveStep = 2 var actualVal = control.stepSize * Math.round(mappedValue / control.stepSize) internal.snappedValue = actualVal } internal.setUpdatedValue = false; } } } MouseArea { anchors.fill: parent enabled: control.interactive onClicked: { var outerRadius = Math.min(control.width, control.height)/ 2 var innerRadius = outerRadius - Math.max(control.trackWidth, 20); var clickedDistance = (mouseX - internal.centerPt.x) * (mouseX - internal.centerPt.x) + (mouseY - internal.centerPt.y) * (mouseY - internal.centerPt.y); var innerRadius2 = (innerRadius * innerRadius); var outerRadius2 = (outerRadius * outerRadius); var isOutOfInnerRadius = clickedDistance > innerRadius2; var inInSideOuterRadius = clickedDistance <= outerRadius2; if (inInSideOuterRadius && isOutOfInnerRadius) { var angleDeg = Math.atan2(mouseY - internal.centerPt.y, mouseX - internal.centerPt.x) * 180 / Math.PI + 90; internal.updateAngle(angleDeg); } } } // Handle Item Item { id: handleItem visible: control.interactive x: control.width / 2 - width / 2 y: control.height / 2 - height / 2 // make sure that the slider handle is always on top as we can set custom track or progress items z: 2 width: control.handleWidth height: control.handleHeight antialiasing: true transform: [ Translate { y: -(Math.min(control.width, control.height) / 2) + Math.max(control.trackWidth, control.progressWidth) / 2 + control.handleVerticalOffset }, Rotation { angle: control.angle origin.x: handleItem.width / 2 origin.y: handleItem.height / 2 } ] MouseArea { id: trackMouse enabled: control.interactive function getVal() { var handlePoint = mapToItem(control, trackMouse.mouseX, trackMouse.mouseY); // angle in degrees var angleDeg = Math.atan2(handlePoint.y - internal.centerPt.y, handlePoint.x - internal.centerPt.x) * 180 / Math.PI + 90; internal.updateAngle(angleDeg); } anchors.fill: parent onPositionChanged: getVal() onClicked: getVal() cursorShape: Qt.ArrowCursor } Loader { id: handleLoader sourceComponent: control.handle ? handle : handleComponent } } /// Default handle component Component { id: handleComponent Rectangle { width: control.handleWidth height: control.handleHeight color: control.handleColor radius: control.handleRadius antialiasing: true } } }
import QtQuick import QtQuick.Controls import "../components" Page { id: darqSpeedo allowedOrientations: Orientation.All // this is specific to SailfishOS and can likely be omitted. // imported from "../components" CircularSlider { id: speedometer interactive: false anchors.centerIn: parent width: 1000 height: width value: 0 startAngle: -108 endAngle: 108 minValue: 0 maxValue: 180 anchors { verticalCenterOffset: 200 verticalCenter: parent.verticalCenter horizontalCenter: parent.horizontalCenter } // Tickmarks around gauge Repeater { model: 37 Rectangle { id: ones width: 6 height: index % 4 === 0 ? 50 : 25 color: index % 4 === 0 ? "white" : "silver" property real angle: index * 6 transform: [ Translate { x: parent.width / 2 - width / 2; y: -155 }, Rotation { origin.x: parent.width / 2 origin.y: parent.height / 2 angle: ones.angle + parent.startAngle } ] } } // Numbers around gauge Repeater { model: 10 Label { id: digits text: index * 20 color: "white" font { bold: true; pixelSize: 48 } property real angle: index * 24 transform: [ Translate { x: parent.width / 2 - width / 2 y: -75 // distance from tickmarks }, Rotation { origin.x: parent.width / 2 origin.y: parent.height / 2 angle: digits.angle + parent.startAngle } ] // keeps numbers horizontal rotation: -digits.angle - parent.startAngle } } // Needle hub Rectangle { anchors.centerIn: speedometer width: 100 height: width radius: width/2 color: "#a0545454" } // Needle/pointer Repeater { model: 1 Rectangle { id: needle width: 20 height: 450 color: "orange" radius: 20 transform: [ Translate { x: speedometer.width / 2 - width / 2 y: 50 // needle distance from centre }, Rotation { origin.x: speedometer.width / 2 origin.y: speedometer.height / 2 angle: speedometer.angle } ] } } // Animation for demo purposes SequentialAnimation on value { id: animation loops: Animation.Infinite running: true NumberAnimation { from: 0; to: 180; duration: 2000 } NumberAnimation { from: 180; to: 0; duration: 2000 } } } }
NOTE: I use
Page
, as my apps are built for my mobile phone using SailfishOS. You don't have to usePage
, you can likely swap it forItem
instead.@Markkyboy I don't mean the code for the entire speedometer. I want to restrict only this part of the code " rotation: 2.25*XXX+225 " to move in the angle range, e.g. from 225 to 135. I want there to be no movement in the range from 136 to 224 degrees. I don't know how to specify this in the code.
-
@Markkyboy I don't mean the code for the entire speedometer. I want to restrict only this part of the code " rotation: 2.25*XXX+225 " to move in the angle range, e.g. from 225 to 135. I want there to be no movement in the range from 136 to 224 degrees. I don't know how to specify this in the code.
@Darq said in Speedometer:
...... I want there to be no movement in the range from 136 to 224 degrees. I don't know how to specify this in the code.
Sorry, right now I have no idea how to get what you want. I guess, like me, you will have to keep trying and playing until you succeed. I found little to no help with my own endeavours but fumbled my way through the code you see above. If I have an epiphany, I'll post it here but I make no promise.
Could you explain why you want such a feature?
-
@Darq said in Speedometer:
...... I want there to be no movement in the range from 136 to 224 degrees. I don't know how to specify this in the code.
Sorry, right now I have no idea how to get what you want. I guess, like me, you will have to keep trying and playing until you succeed. I found little to no help with my own endeavours but fumbled my way through the code you see above. If I have an epiphany, I'll post it here but I make no promise.
Could you explain why you want such a feature?
@Markkyboy Commands like "CircularSlider" don't work for me. This shield is called as png. The only problem is with the limitation of the "Rotation" command, so that the rotation is only within a specific range.
-
J JonB referenced this topic on
-
Rather than leave you completely in the dark, Darq, here's some full code to make a working speedometer. Perhaps from this you can make it do what you want/require.
import QtQuick import QtQuick.Controls Item { id: control property int trackWidth: 20 property int progressWidth: 20 property color trackColor: "#505050" property color progressColor: "#3a4ec4" property bool hideTrack: false property bool hideProgress: false property color handleColor: "#fefefe" property int handleWidth: 80 property int handleHeight: 80 property int handleRadius: 40 property int handleVerticalOffset: 0 property real startAngle: 0 property real endAngle: 360 property real minValue: 0.0 property real maxValue: 1.0 property real value: 0.0 readonly property alias angle: internal.angle property real stepSize: 0 property bool snap: false property Component handle: null readonly property alias pressed: trackMouse.pressed property bool interactive: true property alias cursorShape: trackMouse.cursorShape implicitWidth: 250 implicitHeight: 250 Binding { target: control property: "value" value: control.snap ? internal.snappedValue : internal.mapFromValue(startAngle, endAngle, minValue, maxValue, internal.angleProxy) when: internal.setUpdatedValue } QtObject { id: internal property point centerPt: Qt.point(control.width / 2, control.height / 2) property real baseRadius: Math.min(control.width, control.height) / 2 - Math.max(control.trackWidth, control.progressWidth) / 2 property real actualSpanAngle: control.endAngle - control.startAngle property color transparentColor: "transparent" property color trackColor: control.trackColor property bool setUpdatedValue: false property real angleProxy: control.startAngle property real snappedValue: 0.0 readonly property real angle: internal.mapFromValue(control.minValue, control.maxValue, control.startAngle, control.endAngle, control.value) function mapFromValue(inMin, inMax, outMin, outMax, inValue) { return (inValue - inMin) * (outMax - outMin) / (inMax - inMin) + outMin; } function updateAngle(angleVal) { if (angleVal < 0) angleVal += 360; if ((angleVal >= control.startAngle) && (angleVal <= control.endAngle)) { internal.setUpdatedValue = true; internal.angleProxy = Qt.binding(function() { return angleVal; }); if(control.snap) { var mappedValue = internal.mapFromValue(startAngle, endAngle, minValue, maxValue, internal.angleProxy) var range = control.maxValue - control.minValue var effectiveStep = 2 var actualVal = control.stepSize * Math.round(mappedValue / control.stepSize) internal.snappedValue = actualVal } internal.setUpdatedValue = false; } } } MouseArea { anchors.fill: parent enabled: control.interactive onClicked: { var outerRadius = Math.min(control.width, control.height)/ 2 var innerRadius = outerRadius - Math.max(control.trackWidth, 20); var clickedDistance = (mouseX - internal.centerPt.x) * (mouseX - internal.centerPt.x) + (mouseY - internal.centerPt.y) * (mouseY - internal.centerPt.y); var innerRadius2 = (innerRadius * innerRadius); var outerRadius2 = (outerRadius * outerRadius); var isOutOfInnerRadius = clickedDistance > innerRadius2; var inInSideOuterRadius = clickedDistance <= outerRadius2; if (inInSideOuterRadius && isOutOfInnerRadius) { var angleDeg = Math.atan2(mouseY - internal.centerPt.y, mouseX - internal.centerPt.x) * 180 / Math.PI + 90; internal.updateAngle(angleDeg); } } } // Handle Item Item { id: handleItem visible: control.interactive x: control.width / 2 - width / 2 y: control.height / 2 - height / 2 // make sure that the slider handle is always on top as we can set custom track or progress items z: 2 width: control.handleWidth height: control.handleHeight antialiasing: true transform: [ Translate { y: -(Math.min(control.width, control.height) / 2) + Math.max(control.trackWidth, control.progressWidth) / 2 + control.handleVerticalOffset }, Rotation { angle: control.angle origin.x: handleItem.width / 2 origin.y: handleItem.height / 2 } ] MouseArea { id: trackMouse enabled: control.interactive function getVal() { var handlePoint = mapToItem(control, trackMouse.mouseX, trackMouse.mouseY); // angle in degrees var angleDeg = Math.atan2(handlePoint.y - internal.centerPt.y, handlePoint.x - internal.centerPt.x) * 180 / Math.PI + 90; internal.updateAngle(angleDeg); } anchors.fill: parent onPositionChanged: getVal() onClicked: getVal() cursorShape: Qt.ArrowCursor } Loader { id: handleLoader sourceComponent: control.handle ? handle : handleComponent } } /// Default handle component Component { id: handleComponent Rectangle { width: control.handleWidth height: control.handleHeight color: control.handleColor radius: control.handleRadius antialiasing: true } } }
import QtQuick import QtQuick.Controls import "../components" Page { id: darqSpeedo allowedOrientations: Orientation.All // this is specific to SailfishOS and can likely be omitted. // imported from "../components" CircularSlider { id: speedometer interactive: false anchors.centerIn: parent width: 1000 height: width value: 0 startAngle: -108 endAngle: 108 minValue: 0 maxValue: 180 anchors { verticalCenterOffset: 200 verticalCenter: parent.verticalCenter horizontalCenter: parent.horizontalCenter } // Tickmarks around gauge Repeater { model: 37 Rectangle { id: ones width: 6 height: index % 4 === 0 ? 50 : 25 color: index % 4 === 0 ? "white" : "silver" property real angle: index * 6 transform: [ Translate { x: parent.width / 2 - width / 2; y: -155 }, Rotation { origin.x: parent.width / 2 origin.y: parent.height / 2 angle: ones.angle + parent.startAngle } ] } } // Numbers around gauge Repeater { model: 10 Label { id: digits text: index * 20 color: "white" font { bold: true; pixelSize: 48 } property real angle: index * 24 transform: [ Translate { x: parent.width / 2 - width / 2 y: -75 // distance from tickmarks }, Rotation { origin.x: parent.width / 2 origin.y: parent.height / 2 angle: digits.angle + parent.startAngle } ] // keeps numbers horizontal rotation: -digits.angle - parent.startAngle } } // Needle hub Rectangle { anchors.centerIn: speedometer width: 100 height: width radius: width/2 color: "#a0545454" } // Needle/pointer Repeater { model: 1 Rectangle { id: needle width: 20 height: 450 color: "orange" radius: 20 transform: [ Translate { x: speedometer.width / 2 - width / 2 y: 50 // needle distance from centre }, Rotation { origin.x: speedometer.width / 2 origin.y: speedometer.height / 2 angle: speedometer.angle } ] } } // Animation for demo purposes SequentialAnimation on value { id: animation loops: Animation.Infinite running: true NumberAnimation { from: 0; to: 180; duration: 2000 } NumberAnimation { from: 180; to: 0; duration: 2000 } } } }
NOTE: I use
Page
, as my apps are built for my mobile phone using SailfishOS. You don't have to usePage
, you can likely swap it forItem
instead.@Markkyboy Can You Please specify which file is which file so i can integrate it accordingly like which is main.qml file here
-
@Markkyboy Can You Please specify which file is which file so i can integrate it accordingly like which is main.qml file here