Improve energy views
This commit is contained in:
parent
0549a42032
commit
05d1938167
@ -35,9 +35,9 @@ Item {
|
||||
"presencesensor": "darkblue",
|
||||
"closablesensor": "green",
|
||||
"smartmeterproducer": "lightgreen",
|
||||
"extendedsmartmeterproducer": "lightgreen",
|
||||
"smartmeterconsumer": "orange",
|
||||
"extendedsmartmeterproducer": "blue",
|
||||
"extendedsmartmeterconsumer": "blue",
|
||||
"extendedsmartmeterconsumer": "orange",
|
||||
"heating" : "gainsboro",
|
||||
"thermostat": "dodgerblue",
|
||||
"irrigation": "lightblue",
|
||||
|
||||
@ -36,8 +36,8 @@ ToolButton {
|
||||
property alias color: image.color
|
||||
|
||||
contentItem: Item {
|
||||
height: 20
|
||||
width: 20
|
||||
height: app.iconSize
|
||||
width: app.iconSize
|
||||
ColorIcon {
|
||||
id: image
|
||||
anchors.fill: parent
|
||||
|
||||
@ -72,11 +72,27 @@ Item {
|
||||
viewStartTime: xAxis.min
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
ChartView {
|
||||
id: chartView
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
margins.top: app.iconSize + app.margins
|
||||
margins.bottom: app.margins / 2
|
||||
margins.left: 0
|
||||
margins.right: 0
|
||||
backgroundColor: Style.tileBackgroundColor
|
||||
backgroundRoundness: Style.tileRadius
|
||||
legend.visible: false
|
||||
legend.labelColor: Style.foregroundColor
|
||||
|
||||
titleColor: Style.foregroundColor
|
||||
titleFont.pixelSize: app.largeFont
|
||||
|
||||
animationDuration: 300
|
||||
animationOptions: ChartView.SeriesAnimations
|
||||
|
||||
RowLayout {
|
||||
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
|
||||
anchors { left: parent.left; top: parent.top; right: parent.right; topMargin: app.margins / 2; leftMargin: app.margins; rightMargin: app.margins }
|
||||
|
||||
ColorIcon {
|
||||
Layout.preferredHeight: app.iconSize
|
||||
Layout.preferredWidth: app.iconSize
|
||||
@ -84,21 +100,13 @@ Item {
|
||||
visible: root.iconSource.length > 0
|
||||
color: root.color
|
||||
}
|
||||
|
||||
Led {
|
||||
visible: root.stateType.type.toLowerCase() === "bool"
|
||||
state: root.valueState.value === true ? "on" : "off"
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: root.stateType.type.toLowerCase() === "bool"
|
||||
? root.stateType.displayName
|
||||
: 1.0 * Math.round(Types.toUiValue(root.valueState.value, root.stateType.unit) * Math.pow(10, root.roundTo)) / Math.pow(10, root.roundTo) + " " + Types.toUiUnit(root.stateType.unit)
|
||||
? root.stateType.displayName
|
||||
: 1.0 * Math.round(Types.toUiValue(root.valueState.value, root.stateType.unit) * Math.pow(10, root.roundTo)) / Math.pow(10, root.roundTo) + " " + Types.toUiUnit(root.stateType.unit)
|
||||
font.pixelSize: app.largeFont
|
||||
}
|
||||
|
||||
|
||||
HeaderButton {
|
||||
imageSource: "../images/zoom-out.svg"
|
||||
onClicked: {
|
||||
@ -106,7 +114,6 @@ Item {
|
||||
xAxis.min = newTime;
|
||||
}
|
||||
}
|
||||
|
||||
HeaderButton {
|
||||
imageSource: "../images/zoom-in.svg"
|
||||
enabled: xAxis.timeDiff > (60 * 30)
|
||||
@ -117,333 +124,318 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
ChartView {
|
||||
id: chartView
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
margins.top: 0
|
||||
margins.bottom: 0
|
||||
margins.left: 0
|
||||
margins.right: 0
|
||||
backgroundColor: Material.background
|
||||
legend.visible: false
|
||||
legend.labelColor: Style.foregroundColor
|
||||
|
||||
animationDuration: 300
|
||||
animationOptions: ChartView.SeriesAnimations
|
||||
|
||||
ValueAxis {
|
||||
id: yAxis
|
||||
max: {
|
||||
switch (root.stateType.type.toLowerCase()) {
|
||||
case "bool":
|
||||
return 1;
|
||||
default:
|
||||
Math.ceil(logsModelNg.maxValue + Math.abs(logsModelNg.maxValue * .05))
|
||||
}
|
||||
}
|
||||
min: Math.floor(logsModelNg.minValue - Math.abs(logsModelNg.minValue * .05))
|
||||
// onMinChanged: applyNiceNumbers();
|
||||
// onMaxChanged: applyNiceNumbers();
|
||||
labelsFont.pixelSize: app.smallFont
|
||||
labelFormat: {
|
||||
switch (root.stateType.type.toLowerCase()) {
|
||||
case "bool":
|
||||
return "x";
|
||||
default:
|
||||
return "%d";
|
||||
}
|
||||
}
|
||||
labelsColor: Style.foregroundColor
|
||||
tickCount: root.stateType.type.toLowerCase() === "bool" ? 2 : chartView.height / 40
|
||||
color: Qt.rgba(Style.foregroundColor.r, Style.foregroundColor.g, Style.foregroundColor.b, .2)
|
||||
gridLineColor: color
|
||||
}
|
||||
|
||||
ValueAxis {
|
||||
id: connectedAxis
|
||||
min: 0
|
||||
max: 1
|
||||
visible: false
|
||||
}
|
||||
|
||||
DateTimeAxis {
|
||||
id: xAxis
|
||||
gridVisible: false
|
||||
color: Qt.rgba(Style.foregroundColor.r, Style.foregroundColor.g, Style.foregroundColor.b, .2)
|
||||
tickCount: chartView.width / 70
|
||||
labelsFont.pixelSize: app.smallFont
|
||||
labelsColor: Style.foregroundColor
|
||||
property int timeDiff: (xAxis.max.getTime() - xAxis.min.getTime()) / 1000
|
||||
|
||||
function getTimeSpanString() {
|
||||
var td = Math.round(timeDiff)
|
||||
if (td < 60) {
|
||||
return qsTr("%n seconds", "", td);
|
||||
}
|
||||
td = Math.round(td / 60)
|
||||
if (td < 60) {
|
||||
return qsTr("%n minutes", "", td);
|
||||
}
|
||||
td = Math.round(td / 60)
|
||||
if (td < 48) {
|
||||
return qsTr("%n hours", "", td);
|
||||
}
|
||||
td = Math.round(td / 24);
|
||||
if (td < 14) {
|
||||
return qsTr("%n days", "", td);
|
||||
}
|
||||
td = Math.round(td / 7)
|
||||
if (td < 9) {
|
||||
return qsTr("%n weeks", "", td);
|
||||
}
|
||||
td = Math.round(td * 7 / 30)
|
||||
if (td < 24) {
|
||||
return qsTr("%n months", "", td);
|
||||
}
|
||||
td = Math.round(td * 30 / 356)
|
||||
return qsTr("%n years", "", td)
|
||||
}
|
||||
|
||||
titleText: {
|
||||
if (xAxis.min.getYear() === xAxis.max.getYear()
|
||||
&& xAxis.min.getMonth() === xAxis.max.getMonth()
|
||||
&& xAxis.min.getDate() === xAxis.max.getDate()) {
|
||||
return Qt.formatDate(xAxis.min) + " (" + getTimeSpanString() + ")"
|
||||
}
|
||||
return Qt.formatDate(xAxis.min) + " - " + Qt.formatDate(xAxis.max) + " (" + getTimeSpanString() + ")"
|
||||
}
|
||||
titleBrush: Style.foregroundColor
|
||||
format: {
|
||||
if (timeDiff < 60) { // one minute
|
||||
return "mm:ss"
|
||||
}
|
||||
if (timeDiff < 60 * 60) { // one hour
|
||||
return "hh:mm"
|
||||
}
|
||||
if (timeDiff < 60 * 60 * 24 * 2) { // two day
|
||||
return "hh:mm"
|
||||
}
|
||||
if (timeDiff < 60 * 60 * 24 * 7) { // one week
|
||||
return "ddd hh:mm"
|
||||
}
|
||||
if (timeDiff < 60 * 60 * 24 * 7 * 30) { // one month
|
||||
return "dd.MM."
|
||||
}
|
||||
return "MMM yy"
|
||||
}
|
||||
|
||||
min: {
|
||||
var date = new Date();
|
||||
date.setTime(date.getTime() - (1000 * 60 * 60 * 6) + 2000);
|
||||
return date;
|
||||
}
|
||||
max: {
|
||||
var date = new Date();
|
||||
date.setTime(date.getTime() + 2000)
|
||||
return date;
|
||||
ValueAxis {
|
||||
id: yAxis
|
||||
max: {
|
||||
switch (root.stateType.type.toLowerCase()) {
|
||||
case "bool":
|
||||
return 1;
|
||||
default:
|
||||
Math.ceil(logsModelNg.maxValue + Math.abs(logsModelNg.maxValue * .05))
|
||||
}
|
||||
}
|
||||
|
||||
AreaSeries {
|
||||
axisX: xAxis
|
||||
axisY: connectedAxis
|
||||
name: qsTr("Not connected")
|
||||
visible: root.hasConnectable
|
||||
upperSeries: LineSeries {
|
||||
XYPoint {x: xAxis.min.getTime(); y: 1}
|
||||
XYPoint {x: xAxis.max.getTime(); y: 1}
|
||||
min: Math.floor(logsModelNg.minValue - Math.abs(logsModelNg.minValue * .05))
|
||||
// onMinChanged: applyNiceNumbers();
|
||||
// onMaxChanged: applyNiceNumbers();
|
||||
labelsFont.pixelSize: app.smallFont
|
||||
labelFormat: {
|
||||
switch (root.stateType.type.toLowerCase()) {
|
||||
case "bool":
|
||||
return "x";
|
||||
default:
|
||||
return "%d";
|
||||
}
|
||||
}
|
||||
labelsColor: Style.foregroundColor
|
||||
tickCount: root.stateType.type.toLowerCase() === "bool" ? 2 : chartView.height / 40
|
||||
color: Qt.rgba(Style.foregroundColor.r, Style.foregroundColor.g, Style.foregroundColor.b, .2)
|
||||
gridLineColor: color
|
||||
}
|
||||
|
||||
lowerSeries: LineSeries {
|
||||
id: connectedLineSeries
|
||||
onPointAdded: {
|
||||
var newPoint = connectedLineSeries.at(index)
|
||||
// print("pointadded", newPoint.x, newPoint.y)
|
||||
}
|
||||
ValueAxis {
|
||||
id: connectedAxis
|
||||
min: 0
|
||||
max: 1
|
||||
visible: false
|
||||
}
|
||||
|
||||
DateTimeAxis {
|
||||
id: xAxis
|
||||
gridVisible: false
|
||||
color: Qt.rgba(Style.foregroundColor.r, Style.foregroundColor.g, Style.foregroundColor.b, .2)
|
||||
tickCount: chartView.width / 70
|
||||
labelsFont.pixelSize: app.smallFont
|
||||
labelsColor: Style.foregroundColor
|
||||
property int timeDiff: (xAxis.max.getTime() - xAxis.min.getTime()) / 1000
|
||||
|
||||
function getTimeSpanString() {
|
||||
var td = Math.round(timeDiff)
|
||||
if (td < 60) {
|
||||
return qsTr("%n seconds", "", td);
|
||||
}
|
||||
color: "#55ff0000"
|
||||
borderWidth: 0
|
||||
td = Math.round(td / 60)
|
||||
if (td < 60) {
|
||||
return qsTr("%n minutes", "", td);
|
||||
}
|
||||
td = Math.round(td / 60)
|
||||
if (td < 48) {
|
||||
return qsTr("%n hours", "", td);
|
||||
}
|
||||
td = Math.round(td / 24);
|
||||
if (td < 14) {
|
||||
return qsTr("%n days", "", td);
|
||||
}
|
||||
td = Math.round(td / 7)
|
||||
if (td < 9) {
|
||||
return qsTr("%n weeks", "", td);
|
||||
}
|
||||
td = Math.round(td * 7 / 30)
|
||||
if (td < 24) {
|
||||
return qsTr("%n months", "", td);
|
||||
}
|
||||
td = Math.round(td * 30 / 356)
|
||||
return qsTr("%n years", "", td)
|
||||
}
|
||||
|
||||
AreaSeries {
|
||||
id: mainSeries
|
||||
axisX: xAxis
|
||||
axisY: yAxis
|
||||
name: root.stateType.displayName
|
||||
borderColor: root.color
|
||||
borderWidth: 4
|
||||
lowerSeries: LineSeries {
|
||||
id: lineSeries0
|
||||
XYPoint { x: xAxis.max.getTime(); y: 0 }
|
||||
XYPoint { x: xAxis.min.getTime(); y: 0 }
|
||||
titleText: {
|
||||
if (xAxis.min.getYear() === xAxis.max.getYear()
|
||||
&& xAxis.min.getMonth() === xAxis.max.getMonth()
|
||||
&& xAxis.min.getDate() === xAxis.max.getDate()) {
|
||||
return Qt.formatDate(xAxis.min) + " (" + getTimeSpanString() + ")"
|
||||
}
|
||||
return Qt.formatDate(xAxis.min) + " - " + Qt.formatDate(xAxis.max) + " (" + getTimeSpanString() + ")"
|
||||
}
|
||||
titleBrush: Style.foregroundColor
|
||||
format: {
|
||||
if (timeDiff < 60) { // one minute
|
||||
return "mm:ss"
|
||||
}
|
||||
if (timeDiff < 60 * 60) { // one hour
|
||||
return "hh:mm"
|
||||
}
|
||||
if (timeDiff < 60 * 60 * 24 * 2) { // two day
|
||||
return "hh:mm"
|
||||
}
|
||||
if (timeDiff < 60 * 60 * 24 * 7) { // one week
|
||||
return "ddd hh:mm"
|
||||
}
|
||||
if (timeDiff < 60 * 60 * 24 * 7 * 30) { // one month
|
||||
return "dd.MM."
|
||||
}
|
||||
return "MMM yy"
|
||||
}
|
||||
|
||||
min: {
|
||||
var date = new Date();
|
||||
date.setTime(date.getTime() - (1000 * 60 * 60 * 6) + 2000);
|
||||
return date;
|
||||
}
|
||||
max: {
|
||||
var date = new Date();
|
||||
date.setTime(date.getTime() + 2000)
|
||||
return date;
|
||||
}
|
||||
}
|
||||
|
||||
AreaSeries {
|
||||
axisX: xAxis
|
||||
axisY: connectedAxis
|
||||
name: qsTr("Not connected")
|
||||
visible: root.hasConnectable
|
||||
upperSeries: LineSeries {
|
||||
XYPoint {x: xAxis.min.getTime(); y: 1}
|
||||
XYPoint {x: xAxis.max.getTime(); y: 1}
|
||||
}
|
||||
|
||||
lowerSeries: LineSeries {
|
||||
id: connectedLineSeries
|
||||
onPointAdded: {
|
||||
var newPoint = connectedLineSeries.at(index)
|
||||
// print("pointadded", newPoint.x, newPoint.y)
|
||||
}
|
||||
|
||||
upperSeries: LineSeries {
|
||||
id: lineSeries1
|
||||
onPointAdded: {
|
||||
var newPoint = lineSeries1.at(index)
|
||||
// print("pointadded", newPoint.x, newPoint.y)
|
||||
}
|
||||
color: "#55ff0000"
|
||||
borderWidth: 0
|
||||
}
|
||||
|
||||
if (newPoint.x > lineSeries0.at(0).x) {
|
||||
lineSeries0.replace(0, newPoint.x, 0)
|
||||
}
|
||||
if (newPoint.x < lineSeries0.at(1).x) {
|
||||
lineSeries0.replace(1, newPoint.x, 0)
|
||||
}
|
||||
AreaSeries {
|
||||
id: mainSeries
|
||||
axisX: xAxis
|
||||
axisY: yAxis
|
||||
name: root.stateType.displayName
|
||||
borderColor: root.color
|
||||
borderWidth: 4
|
||||
lowerSeries: LineSeries {
|
||||
id: lineSeries0
|
||||
XYPoint { x: xAxis.max.getTime(); y: 0 }
|
||||
XYPoint { x: xAxis.min.getTime(); y: 0 }
|
||||
}
|
||||
|
||||
if (newPoint.x <= xAxis.max.getTime() || logsModelNg.busy) {
|
||||
return;
|
||||
}
|
||||
|
||||
var diffMaxToNew = newPoint.x - xAxis.max.getTime();
|
||||
if (diffMaxToNew < 1000 * 60 * 5) {
|
||||
chartView.animationOptions = ChartView.NoAnimation
|
||||
var newMin = xAxis.min.getTime() + diffMaxToNew;
|
||||
xAxis.max = new Date(newPoint.x);
|
||||
xAxis.min = new Date(newMin)
|
||||
chartView.animationOptions = ChartView.SeriesAnimations
|
||||
}
|
||||
upperSeries: LineSeries {
|
||||
id: lineSeries1
|
||||
onPointAdded: {
|
||||
var newPoint = lineSeries1.at(index)
|
||||
// print("pointadded", newPoint.x, newPoint.y)
|
||||
|
||||
if (newPoint.x > lineSeries0.at(0).x) {
|
||||
lineSeries0.replace(0, newPoint.x, 0)
|
||||
}
|
||||
if (newPoint.x < lineSeries0.at(1).x) {
|
||||
lineSeries0.replace(1, newPoint.x, 0)
|
||||
}
|
||||
}
|
||||
color: Qt.rgba(root.color.r, root.color.g, root.color.b, .3)
|
||||
onHovered: {
|
||||
markClosestPoint(point)
|
||||
}
|
||||
|
||||
function markClosestPoint(point) {
|
||||
if (lineSeries1.count == 0) {
|
||||
if (newPoint.x <= xAxis.max.getTime() || logsModelNg.busy) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (lineSeries1.count == 1) {
|
||||
selectedHighlights.removePoints(0, selectedHighlights.count)
|
||||
selectedHighlights.append(lineSeries1.at(0).x, lineSeries1.at(1).y)
|
||||
return;
|
||||
var diffMaxToNew = newPoint.x - xAxis.max.getTime();
|
||||
if (diffMaxToNew < 1000 * 60 * 5) {
|
||||
chartView.animationOptions = ChartView.NoAnimation
|
||||
var newMin = xAxis.min.getTime() + diffMaxToNew;
|
||||
xAxis.max = new Date(newPoint.x);
|
||||
xAxis.min = new Date(newMin)
|
||||
chartView.animationOptions = ChartView.SeriesAnimations
|
||||
}
|
||||
|
||||
var searchIndex = Math.floor(lineSeries1.count / 2)
|
||||
var previousIndex = 0;
|
||||
var nextIndex = lineSeries1.count - 1;
|
||||
}
|
||||
}
|
||||
color: Qt.rgba(root.color.r, root.color.g, root.color.b, .3)
|
||||
onHovered: {
|
||||
markClosestPoint(point)
|
||||
}
|
||||
|
||||
while (previousIndex + 1 != nextIndex) {
|
||||
if (point.x < lineSeries1.at(searchIndex).x) {
|
||||
previousIndex = searchIndex;
|
||||
} else if (point.x > lineSeries1.at(searchIndex).x) {
|
||||
nextIndex = searchIndex;
|
||||
}
|
||||
searchIndex = previousIndex + Math.floor((nextIndex - previousIndex) / 2);
|
||||
}
|
||||
var diffToPrevious = Math.abs(point.x - lineSeries1.at(previousIndex).x)
|
||||
var diffToNext = Math.abs(point.x - lineSeries1.at(nextIndex).x)
|
||||
var closestPoint = diffToPrevious < diffToNext ? lineSeries1.at(previousIndex) : lineSeries1.at(nextIndex);
|
||||
function markClosestPoint(point) {
|
||||
if (lineSeries1.count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (lineSeries1.count == 1) {
|
||||
selectedHighlights.removePoints(0, selectedHighlights.count)
|
||||
selectedHighlights.append(closestPoint.x, closestPoint.y)
|
||||
selectedHighlights.append(lineSeries1.at(0).x, lineSeries1.at(1).y)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ScatterSeries {
|
||||
id: selectedHighlights
|
||||
color: root.color
|
||||
markerSize: 10
|
||||
borderWidth: 2
|
||||
borderColor: root.color
|
||||
axisX: xAxis
|
||||
axisY: yAxis
|
||||
pointLabelsVisible: root.stateType.type.toLowerCase() !== "bool"
|
||||
pointLabelsColor: Style.foregroundColor
|
||||
pointLabelsFont.pixelSize: app.smallFont
|
||||
pointLabelsFormat: "@yPoint"
|
||||
pointLabelsClipping: false
|
||||
}
|
||||
var searchIndex = Math.floor(lineSeries1.count / 2)
|
||||
var previousIndex = 0;
|
||||
var nextIndex = lineSeries1.count - 1;
|
||||
|
||||
BusyIndicator {
|
||||
anchors.centerIn: parent
|
||||
visible: logsModelNg.busy
|
||||
}
|
||||
|
||||
|
||||
MouseArea {
|
||||
id: scrollMouseArea
|
||||
x: chartView.plotArea.x
|
||||
y: chartView.plotArea.y
|
||||
width: chartView.plotArea.width
|
||||
height: chartView.plotArea.height
|
||||
property int lastX: 0
|
||||
property int startX: 0
|
||||
preventStealing: false
|
||||
|
||||
property bool autoScroll: true
|
||||
|
||||
function scrollRightLimited(dx) {
|
||||
chartView.animationOptions = ChartView.NoAnimation
|
||||
var now = new Date()
|
||||
// if we're already at the limit, don't even start scrolling
|
||||
if (dx < 0 || xAxis.max < now) {
|
||||
chartView.scrollRight(dx)
|
||||
while (previousIndex + 1 != nextIndex) {
|
||||
if (point.x < lineSeries1.at(searchIndex).x) {
|
||||
previousIndex = searchIndex;
|
||||
} else if (point.x > lineSeries1.at(searchIndex).x) {
|
||||
nextIndex = searchIndex;
|
||||
}
|
||||
// figure out if we scrolled too far
|
||||
var overshoot = xAxis.max.getTime() - now.getTime()
|
||||
// print("overshoot is:", overshoot, "oldMax", xAxis.max, "newMax", now, "oldMin", xAxis.min, "newMin", new Date(xAxis.min.getTime() - overshoot))
|
||||
if (overshoot > 0) {
|
||||
var range = xAxis.max - xAxis.min
|
||||
xAxis.max = now
|
||||
xAxis.min = new Date(xAxis.max.getTime() - range)
|
||||
}
|
||||
// If the user scrolled closer than 5 pixels to the right edge, enable autoscroll
|
||||
autoScroll = overshoot > -5;
|
||||
|
||||
chartView.animationOptions = ChartView.SeriesAnimations
|
||||
searchIndex = previousIndex + Math.floor((nextIndex - previousIndex) / 2);
|
||||
}
|
||||
var diffToPrevious = Math.abs(point.x - lineSeries1.at(previousIndex).x)
|
||||
var diffToNext = Math.abs(point.x - lineSeries1.at(nextIndex).x)
|
||||
var closestPoint = diffToPrevious < diffToNext ? lineSeries1.at(previousIndex) : lineSeries1.at(nextIndex);
|
||||
|
||||
function zoomInLimited(dy) {
|
||||
chartView.animationOptions = ChartView.NoAnimation
|
||||
var oldMax = xAxis.max;
|
||||
chartView.scrollRight(dy);
|
||||
xAxis.min = new Date(xAxis.min.getTime() - xAxis.timeDiff * 1000 * 2)
|
||||
chartView.animationOptions = ChartView.SeriesAnimations
|
||||
selectedHighlights.removePoints(0, selectedHighlights.count)
|
||||
selectedHighlights.append(closestPoint.x, closestPoint.y)
|
||||
}
|
||||
}
|
||||
|
||||
ScatterSeries {
|
||||
id: selectedHighlights
|
||||
color: root.color
|
||||
markerSize: 10
|
||||
borderWidth: 2
|
||||
borderColor: root.color
|
||||
axisX: xAxis
|
||||
axisY: yAxis
|
||||
pointLabelsVisible: root.stateType.type.toLowerCase() !== "bool"
|
||||
pointLabelsColor: Style.foregroundColor
|
||||
pointLabelsFont.pixelSize: app.smallFont
|
||||
pointLabelsFormat: "@yPoint"
|
||||
pointLabelsClipping: false
|
||||
}
|
||||
|
||||
BusyIndicator {
|
||||
anchors.centerIn: parent
|
||||
visible: logsModelNg.busy
|
||||
}
|
||||
|
||||
|
||||
MouseArea {
|
||||
id: scrollMouseArea
|
||||
x: chartView.plotArea.x
|
||||
y: chartView.plotArea.y
|
||||
width: chartView.plotArea.width
|
||||
height: chartView.plotArea.height
|
||||
property int lastX: 0
|
||||
property int startX: 0
|
||||
preventStealing: false
|
||||
|
||||
property bool autoScroll: true
|
||||
|
||||
function scrollRightLimited(dx) {
|
||||
chartView.animationOptions = ChartView.NoAnimation
|
||||
var now = new Date()
|
||||
// if we're already at the limit, don't even start scrolling
|
||||
if (dx < 0 || xAxis.max < now) {
|
||||
chartView.scrollRight(dx)
|
||||
}
|
||||
// figure out if we scrolled too far
|
||||
var overshoot = xAxis.max.getTime() - now.getTime()
|
||||
// print("overshoot is:", overshoot, "oldMax", xAxis.max, "newMax", now, "oldMin", xAxis.min, "newMin", new Date(xAxis.min.getTime() - overshoot))
|
||||
if (overshoot > 0) {
|
||||
var range = xAxis.max - xAxis.min
|
||||
xAxis.max = now
|
||||
xAxis.min = new Date(xAxis.max.getTime() - range)
|
||||
}
|
||||
// If the user scrolled closer than 5 pixels to the right edge, enable autoscroll
|
||||
autoScroll = overshoot > -5;
|
||||
|
||||
onPressed: {
|
||||
chartView.animationOptions = ChartView.SeriesAnimations
|
||||
}
|
||||
|
||||
function zoomInLimited(dy) {
|
||||
chartView.animationOptions = ChartView.NoAnimation
|
||||
var oldMax = xAxis.max;
|
||||
chartView.scrollRight(dy);
|
||||
xAxis.min = new Date(xAxis.min.getTime() - xAxis.timeDiff * 1000 * 2)
|
||||
chartView.animationOptions = ChartView.SeriesAnimations
|
||||
}
|
||||
|
||||
onPressed: {
|
||||
lastX = mouse.x
|
||||
startX = mouse.x
|
||||
}
|
||||
onClicked: {
|
||||
var pt = chartView.mapToValue(Qt.point(mouse.x + chartView.plotArea.x, mouse.y + chartView.plotArea.y), mainSeries)
|
||||
mainSeries.markClosestPoint(pt)
|
||||
}
|
||||
|
||||
onWheel: {
|
||||
scrollRightLimited(-wheel.pixelDelta.x)
|
||||
// zoomInLimited(wheel.pixelDelta.y)
|
||||
}
|
||||
|
||||
onPositionChanged: {
|
||||
if (lastX !== mouse.x) {
|
||||
scrollRightLimited(lastX - mouseX)
|
||||
lastX = mouse.x
|
||||
startX = mouse.x
|
||||
}
|
||||
onClicked: {
|
||||
var pt = chartView.mapToValue(Qt.point(mouse.x + chartView.plotArea.x, mouse.y + chartView.plotArea.y), mainSeries)
|
||||
mainSeries.markClosestPoint(pt)
|
||||
}
|
||||
|
||||
onWheel: {
|
||||
scrollRightLimited(-wheel.pixelDelta.x)
|
||||
// zoomInLimited(wheel.pixelDelta.y)
|
||||
if (Math.abs(startX - mouse.x) > 10) {
|
||||
preventStealing = true;
|
||||
}
|
||||
}
|
||||
|
||||
onPositionChanged: {
|
||||
if (lastX !== mouse.x) {
|
||||
scrollRightLimited(lastX - mouseX)
|
||||
lastX = mouse.x
|
||||
}
|
||||
|
||||
if (Math.abs(startX - mouse.x) > 10) {
|
||||
preventStealing = true;
|
||||
}
|
||||
}
|
||||
|
||||
onReleased: preventStealing = false;
|
||||
onReleased: preventStealing = false;
|
||||
|
||||
|
||||
Timer {
|
||||
running: scrollMouseArea.autoScroll
|
||||
interval: 1000
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
scrollMouseArea.scrollRightLimited(10)
|
||||
}
|
||||
Timer {
|
||||
running: scrollMouseArea.autoScroll
|
||||
interval: 1000
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
scrollMouseArea.scrollRightLimited(10)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -38,6 +38,11 @@ import "../customviews"
|
||||
DevicePageBase {
|
||||
id: root
|
||||
|
||||
readonly property State totalEnergyConsumedState: root.thing.stateByName("totalEnergyConsumed")
|
||||
readonly property StateType totalEnergyConsumedStateType: root.thing.thingClass.stateTypes.findByName("totalEnergyConsumed")
|
||||
readonly property State totalEnergyProducedState: root.thing.stateByName("totalEnergyProduced")
|
||||
readonly property StateType totalEnergyProducedStateType: root.thing.thingClass.stateTypes.findByName("totalEnergyProduced")
|
||||
|
||||
Flickable {
|
||||
anchors.fill: parent
|
||||
contentHeight: contentGrid.height
|
||||
@ -45,34 +50,48 @@ DevicePageBase {
|
||||
|
||||
GridLayout {
|
||||
id: contentGrid
|
||||
width: parent.width
|
||||
columns: Math.min(width / 300, contentModel.count)
|
||||
width: parent.width - app.margins
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
columns: 1
|
||||
|
||||
Repeater {
|
||||
model: ListModel {
|
||||
id: contentModel
|
||||
Component.onCompleted: {
|
||||
if (root.deviceClass.interfaces.indexOf("extendedsmartmeterproducer") >= 0
|
||||
|| root.deviceClass.interfaces.indexOf("extendedsmartmeterconsumer") >= 0) {
|
||||
append( {interface: "extendedsmartmeterproducer", stateTypeName: "currentPower" })
|
||||
}
|
||||
if (root.deviceClass.interfaces.indexOf("smartmeterproducer") >= 0) {
|
||||
append( {interface: "smartmeterproducer", stateTypeName: "totalEnergyProduced" })
|
||||
}
|
||||
if (root.deviceClass.interfaces.indexOf("smartmeterconsumer") >= 0) {
|
||||
append( {interface: "smartmeterconsumer", stateTypeName: "totalEnergyConsumed" })
|
||||
}
|
||||
print("shown graphs are", count)
|
||||
BigTile {
|
||||
Layout.preferredWidth: contentGrid.width / contentGrid.columns
|
||||
showHeader: false
|
||||
contentItem: RowLayout {
|
||||
ColorIcon {
|
||||
Layout.preferredHeight: app.iconSize
|
||||
Layout.preferredWidth: app.iconSize
|
||||
name: app.interfaceToIcon("smartmeterconsumer")
|
||||
color: app.interfaceToColor("smartmeterconsumer")
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: root.totalEnergyConsumedState.value.toFixed(2) + " " + root.totalEnergyConsumedStateType.unitString
|
||||
font.pixelSize: app.largeFont
|
||||
}
|
||||
ColorIcon {
|
||||
Layout.preferredHeight: app.iconSize
|
||||
Layout.preferredWidth: app.iconSize
|
||||
name: app.interfaceToIcon("smartmeterproducer")
|
||||
color: app.interfaceToColor("smartmeterproducer")
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: root.totalEnergyProducedState.value.toFixed(2) + " " + root.totalEnergyProducedStateType.unitString
|
||||
font.pixelSize: app.largeFont
|
||||
}
|
||||
}
|
||||
delegate: GenericTypeGraph {
|
||||
Layout.preferredWidth: contentGrid.width / contentGrid.columns
|
||||
device: root.device
|
||||
stateType: root.deviceClass.stateTypes.findByName(model.stateTypeName)
|
||||
color: app.interfaceToColor(model.interface)
|
||||
iconSource: app.interfaceToIcon(model.interface)
|
||||
roundTo: 5
|
||||
}
|
||||
}
|
||||
|
||||
GenericTypeGraph {
|
||||
Layout.preferredWidth: contentGrid.width / contentGrid.columns
|
||||
device: root.device
|
||||
stateType: root.deviceClass.stateTypes.findByName("currentPower")
|
||||
color: app.interfaceToColor("smartmeterconsumer")
|
||||
iconSource: app.interfaceToIcon("smartmeterconsumer")
|
||||
roundTo: 5
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user